1 // -*- mode: C++; c-file-style: "cc-mode" -*-
2 //*************************************************************************
3 // DESCRIPTION: Verilator: Expression width calculations
4 //
5 // Code available from: https://verilator.org
6 //
7 //*************************************************************************
8 //
9 // Copyright 2003-2021 by Wilson Snyder. This program is free software; you
10 // can redistribute it and/or modify it under the terms of either the GNU
11 // Lesser General Public License Version 3 or the Perl Artistic License
12 // Version 2.0.
13 // SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
14 //
15 //*************************************************************************
16 // V3Width's Transformations:
17 //      Top down traversal:
18 //          Determine width of sub-expressions
19 //              width() = # bits upper expression wants, 0 for anything-goes
20 //              widthUnsized() = # bits for unsized constant, or 0 if it's sized
21 //              widthMin() = Alternative acceptable width for linting, or width() if sized
22 //              Determine this subop's width, can be either:
23 //                  Fixed width X
24 //                  Unsized, min width X   ('d5 is unsized, min 3 bits.)
25 //              Pass up:
26 //                  width() = # bits this expression generates
27 //                  widthSized() = true if all constants sized, else false
28 //          Compute size of this expression
29 //          Lint warn about mismatches
30 //              If expr size != subop fixed, bad
31 //              If expr size  < subop unsized minimum, bad
32 //              If expr size != subop, edit netlist
33 //                      For == and similar ops, if multibit underneath, add a REDOR
34 //                      If subop larger, add a EXTRACT
35 //                      If subop smaller, add a EXTEND
36 //          Pass size to sub-expressions if required (+/-* etc)
37 //              FINAL = true.
38 //              Subexpressions lint and extend as needed
39 //
40 //*************************************************************************
41 // Signedness depends on:
42 //      Decimal numbers are signed
43 //      Based numbers are unsigned unless 's' prefix
44 //      Comparison results are unsigned
45 //      Bit&Part selects are unsigned, even if whole
46 //      Concatenates are unsigned
47 //      Ignore signedness of self-determined:
48 //              shift rhs, ** rhs, x?: lhs, concat and replicate members
49 //      Else, if any operand unsigned, output unsigned
50 //
51 // Real number rules:
52 //      Real numbers are real (duh)
53 //      Reals convert to integers by rounding
54 //      Reals init to 0.0
55 //      Logicals convert compared to zero
56 //      If any operand is real, result is real
57 //*************************************************************************
58 // V3Width is the only visitor that uses vup.  We could switch to using userp,
59 // though note some iterators operate on next() and so would need to pass the
60 // same value on each nextp().
61 //*************************************************************************
62 // See notes in internal.txt about misuse of iterateAndNext and use of
63 // iterateSubtreeReturnEdits.
64 //*************************************************************************
65 
66 #include "config_build.h"
67 #include "verilatedos.h"
68 
69 #include "V3Global.h"
70 #include "V3Width.h"
71 #include "V3Number.h"
72 #include "V3Const.h"
73 #include "V3Randomize.h"
74 #include "V3String.h"
75 #include "V3Task.h"
76 
77 #include <algorithm>
78 
79 // More code; this file was getting too large; see actions there
80 #define VERILATOR_V3WIDTH_CPP_
81 #include "V3WidthCommit.h"
82 
83 //######################################################################
84 
85 enum Stage : uint8_t {
86     PRELIM = 1,
87     FINAL = 2,
88     BOTH = 3
89 };  // Numbers are a bitmask <0>=prelim, <1>=final
operator <<(std::ostream & str,const Stage & rhs)90 std::ostream& operator<<(std::ostream& str, const Stage& rhs) {
91     return str << ("-PFB"[static_cast<int>(rhs)]);
92 }
93 
94 enum Determ : uint8_t {
95     SELF,  // Self-determined
96     CONTEXT,  // Context-determined
97     ASSIGN  // Assignment-like where sign comes from RHS only
98 };
operator <<(std::ostream & str,const Determ & rhs)99 std::ostream& operator<<(std::ostream& str, const Determ& rhs) {
100     static const char* const s_det[] = {"SELF", "CNTX", "ASSN"};
101     return str << s_det[rhs];
102 }
103 
104 enum Castable : uint8_t { UNSUPPORTED, COMPATIBLE, DYNAMIC_ENUM, DYNAMIC_CLASS, INCOMPATIBLE };
operator <<(std::ostream & str,const Castable & rhs)105 std::ostream& operator<<(std::ostream& str, const Castable& rhs) {
106     static const char* const s_det[] = {"UNSUP", "COMPAT", "DYN_ENUM", "DYN_CLS", "INCOMPAT"};
107     return str << s_det[rhs];
108 }
109 
110 //######################################################################
111 // Width state, as a visitor of each AstNode
112 
113 class WidthVP final {
114     // Parameters to pass down hierarchy with visit functions.
115     AstNodeDType* const m_dtypep;  // Parent's data type to resolve to
116     const Stage m_stage;  // If true, report errors
117 public:
WidthVP(AstNodeDType * dtypep,Stage stage)118     WidthVP(AstNodeDType* dtypep, Stage stage)
119         : m_dtypep{dtypep}
120         , m_stage{stage} {
121         // Prelim doesn't look at assignments, so shouldn't need a dtype,
122         // however AstPattern uses them
123     }
WidthVP(Determ determ,Stage stage)124     WidthVP(Determ determ, Stage stage)
125         : m_dtypep{nullptr}
126         , m_stage{stage} {
127         if (determ != SELF && stage != PRELIM)
128             v3fatalSrc("Context-determined width request only allowed as prelim step");
129     }
p()130     WidthVP* p() { return this; }
selfDtm() const131     bool selfDtm() const { return m_dtypep == nullptr; }
dtypep() const132     AstNodeDType* dtypep() const {
133         // Detect where overrideDType is probably the intended call
134         if (!m_dtypep) v3fatalSrc("Width dtype request on self-determined or preliminary VUP");
135         return m_dtypep;
136     }
dtypeNullp() const137     AstNodeDType* dtypeNullp() const { return m_dtypep; }
dtypeNullSkipRefp() const138     AstNodeDType* dtypeNullSkipRefp() const {
139         AstNodeDType* dtp = dtypeNullp();
140         if (dtp) dtp = dtp->skipRefp();
141         return dtp;
142     }
dtypeOverridep(AstNodeDType * defaultp) const143     AstNodeDType* dtypeOverridep(AstNodeDType* defaultp) const {
144         if (m_stage == PRELIM) v3fatalSrc("Parent dtype should be a final-stage action");
145         return m_dtypep ? m_dtypep : defaultp;
146     }
width() const147     int width() const {
148         if (!m_dtypep) v3fatalSrc("Width request on self-determined or preliminary VUP");
149         return m_dtypep->width();
150     }
widthMin() const151     int widthMin() const {
152         if (!m_dtypep) v3fatalSrc("Width request on self-determined or preliminary VUP");
153         return m_dtypep->widthMin();
154     }
prelim() const155     bool prelim() const { return m_stage & PRELIM; }
final() const156     bool final() const { return m_stage & FINAL; }
dump(std::ostream & str) const157     void dump(std::ostream& str) const {
158         if (!m_dtypep) {
159             str << "  VUP(s=" << m_stage << ",self)";
160         } else {
161             str << "  VUP(s=" << m_stage << ",dt=" << cvtToHex(dtypep());
162             dtypep()->dumpSmall(str);
163             str << ")";
164         }
165     }
166 };
operator <<(std::ostream & str,const WidthVP * vup)167 std::ostream& operator<<(std::ostream& str, const WidthVP* vup) {
168     if (vup) vup->dump(str);
169     return str;
170 }
171 
172 //######################################################################
173 
174 class WidthClearVisitor final {
175     // Rather than a AstNVisitor, can just quickly touch every node
clearWidthRecurse(AstNode * nodep)176     void clearWidthRecurse(AstNode* nodep) {
177         for (; nodep; nodep = nodep->nextp()) {
178             nodep->didWidth(false);
179             if (nodep->op1p()) clearWidthRecurse(nodep->op1p());
180             if (nodep->op2p()) clearWidthRecurse(nodep->op2p());
181             if (nodep->op3p()) clearWidthRecurse(nodep->op3p());
182             if (nodep->op4p()) clearWidthRecurse(nodep->op4p());
183         }
184     }
185 
186 public:
187     // CONSTRUCTORS
WidthClearVisitor(AstNetlist * nodep)188     explicit WidthClearVisitor(AstNetlist* nodep) { clearWidthRecurse(nodep); }
189     virtual ~WidthClearVisitor() = default;
190 };
191 
192 //######################################################################
193 
194 #define accept in_WidthVisitor_use_AstNode_iterate_instead_of_AstNode_accept
195 
196 //######################################################################
197 
198 class WidthVisitor final : public AstNVisitor {
199 private:
200     // TYPES
201     using TableMap = std::map<std::pair<const AstNodeDType*, AstAttrType>, AstVar*>;
202     using PatVecMap = std::map<int, AstPatMember*>;
203 
204     // STATE
205     WidthVP* m_vup = nullptr;  // Current node state
206     const AstCell* m_cellp = nullptr;  // Current cell for arrayed instantiations
207     const AstNodeFTask* m_ftaskp = nullptr;  // Current function/task
208     const AstNodeProcedure* m_procedurep = nullptr;  // Current final/always
209     const AstWith* m_withp = nullptr;  // Current 'with' statement
210     const AstFunc* m_funcp = nullptr;  // Current function
211     const AstAttrOf* m_attrp = nullptr;  // Current attribute
212     const bool m_paramsOnly;  // Computing parameter value; limit operation
213     const bool m_doGenerate;  // Do errors later inside generate statement
214     int m_dtTables = 0;  // Number of created data type tables
215     TableMap m_tableMap;  // Created tables so can remove duplicates
216     std::map<const AstNodeDType*, AstQueueDType*>
217         m_queueDTypeIndexed;  // Queues with given index type
218 
219     // ENUMS
220     enum ExtendRule : uint8_t {
221         EXTEND_EXP,  // Extend if expect sign and node signed, e.g. node=y in ADD(x,y), "x + y"
222         EXTEND_ZERO,  // Extend with zeros. e.g. node=y in EQ(x,y), "x == y"
223         EXTEND_LHS,  // Extend with sign if node signed. e.g. node=y in ASSIGN(y,x), "x = y"
224         EXTEND_OFF  // No extension
225     };
226 
227     // METHODS
debug()228     static int debug() { return V3Width::debug(); }
229 
230     // VISITORS
231     //   Naming:  width_O{outputtype}_L{lhstype}_R{rhstype}_W{widthing}_S{signing}
232     //          Where type:
233     //                  _O1=boolean (width 1 unsigned)
234     //                  _Ou=unsigned
235     //                  _Os=signed
236     //                  _Ous=unsigned or signed
237     //                  _Or=real
238     //                  _Ox=anything
239 
240     // Widths: 1 bit out, lhs 1 bit; Real: converts via compare with 0
visit(AstLogNot * nodep)241     virtual void visit(AstLogNot* nodep) override { visit_log_not(nodep); }
242     // Widths: 1 bit out, lhs 1 bit, rhs 1 bit; Real: converts via compare with 0
visit(AstLogAnd * nodep)243     virtual void visit(AstLogAnd* nodep) override { visit_log_and_or(nodep); }
visit(AstLogOr * nodep)244     virtual void visit(AstLogOr* nodep) override { visit_log_and_or(nodep); }
visit(AstLogEq * nodep)245     virtual void visit(AstLogEq* nodep) override {
246         // Conversion from real not in IEEE, but a fallout
247         visit_log_and_or(nodep);
248     }
visit(AstLogIf * nodep)249     virtual void visit(AstLogIf* nodep) override {
250         // Conversion from real not in IEEE, but a fallout
251         visit_log_and_or(nodep);
252     }
253 
254     // Widths: 1 bit out, Any width lhs
visit(AstRedAnd * nodep)255     virtual void visit(AstRedAnd* nodep) override { visit_red_and_or(nodep); }
visit(AstRedOr * nodep)256     virtual void visit(AstRedOr* nodep) override { visit_red_and_or(nodep); }
visit(AstRedXor * nodep)257     virtual void visit(AstRedXor* nodep) override { visit_red_and_or(nodep); }
visit(AstOneHot * nodep)258     virtual void visit(AstOneHot* nodep) override { visit_red_and_or(nodep); }
visit(AstOneHot0 * nodep)259     virtual void visit(AstOneHot0* nodep) override { visit_red_and_or(nodep); }
visit(AstIsUnknown * nodep)260     virtual void visit(AstIsUnknown* nodep) override {
261         visit_red_unknown(nodep);  // Allow real
262     }
263 
264     // These have different node types, as they operate differently
265     // Must add to case statement below,
266     // Widths: 1 bit out, lhs width == rhs width.  real if lhs|rhs real
visit(AstEq * nodep)267     virtual void visit(AstEq* nodep) override { visit_cmp_eq_gt(nodep, true); }
visit(AstNeq * nodep)268     virtual void visit(AstNeq* nodep) override { visit_cmp_eq_gt(nodep, true); }
visit(AstGt * nodep)269     virtual void visit(AstGt* nodep) override { visit_cmp_eq_gt(nodep, true); }
visit(AstGte * nodep)270     virtual void visit(AstGte* nodep) override { visit_cmp_eq_gt(nodep, true); }
visit(AstLt * nodep)271     virtual void visit(AstLt* nodep) override { visit_cmp_eq_gt(nodep, true); }
visit(AstLte * nodep)272     virtual void visit(AstLte* nodep) override { visit_cmp_eq_gt(nodep, true); }
visit(AstGtS * nodep)273     virtual void visit(AstGtS* nodep) override { visit_cmp_eq_gt(nodep, true); }
visit(AstGteS * nodep)274     virtual void visit(AstGteS* nodep) override { visit_cmp_eq_gt(nodep, true); }
visit(AstLtS * nodep)275     virtual void visit(AstLtS* nodep) override { visit_cmp_eq_gt(nodep, true); }
visit(AstLteS * nodep)276     virtual void visit(AstLteS* nodep) override { visit_cmp_eq_gt(nodep, true); }
visit(AstEqCase * nodep)277     virtual void visit(AstEqCase* nodep) override { visit_cmp_eq_gt(nodep, true); }
visit(AstNeqCase * nodep)278     virtual void visit(AstNeqCase* nodep) override { visit_cmp_eq_gt(nodep, true); }
279     // ...    These comparisons don't allow reals
visit(AstEqWild * nodep)280     virtual void visit(AstEqWild* nodep) override { visit_cmp_eq_gt(nodep, false); }
visit(AstNeqWild * nodep)281     virtual void visit(AstNeqWild* nodep) override { visit_cmp_eq_gt(nodep, false); }
282     // ...    Real compares
visit(AstEqD * nodep)283     virtual void visit(AstEqD* nodep) override { visit_cmp_real(nodep); }
visit(AstNeqD * nodep)284     virtual void visit(AstNeqD* nodep) override { visit_cmp_real(nodep); }
visit(AstLtD * nodep)285     virtual void visit(AstLtD* nodep) override { visit_cmp_real(nodep); }
visit(AstLteD * nodep)286     virtual void visit(AstLteD* nodep) override { visit_cmp_real(nodep); }
visit(AstGtD * nodep)287     virtual void visit(AstGtD* nodep) override { visit_cmp_real(nodep); }
visit(AstGteD * nodep)288     virtual void visit(AstGteD* nodep) override { visit_cmp_real(nodep); }
289     // ...    String compares
visit(AstEqN * nodep)290     virtual void visit(AstEqN* nodep) override { visit_cmp_string(nodep); }
visit(AstNeqN * nodep)291     virtual void visit(AstNeqN* nodep) override { visit_cmp_string(nodep); }
visit(AstLtN * nodep)292     virtual void visit(AstLtN* nodep) override { visit_cmp_string(nodep); }
visit(AstLteN * nodep)293     virtual void visit(AstLteN* nodep) override { visit_cmp_string(nodep); }
visit(AstGtN * nodep)294     virtual void visit(AstGtN* nodep) override { visit_cmp_string(nodep); }
visit(AstGteN * nodep)295     virtual void visit(AstGteN* nodep) override { visit_cmp_string(nodep); }
296 
297     // Widths: out width = lhs width = rhs width
298     // Signed: Output signed iff LHS & RHS signed.
299     // Real: Not allowed
visit(AstAnd * nodep)300     virtual void visit(AstAnd* nodep) override { visit_boolmath_and_or(nodep); }
visit(AstOr * nodep)301     virtual void visit(AstOr* nodep) override { visit_boolmath_and_or(nodep); }
visit(AstXor * nodep)302     virtual void visit(AstXor* nodep) override { visit_boolmath_and_or(nodep); }
visit(AstBufIf1 * nodep)303     virtual void visit(AstBufIf1* nodep) override {
304         visit_boolmath_and_or(nodep);
305     }  // Signed behavior changing in 3.814
306     // Width: Max(Lhs,Rhs) sort of.
307     // Real: If either side real
308     // Signed: If both sides real
visit(AstAdd * nodep)309     virtual void visit(AstAdd* nodep) override { visit_add_sub_replace(nodep, true); }
visit(AstSub * nodep)310     virtual void visit(AstSub* nodep) override { visit_add_sub_replace(nodep, true); }
visit(AstDiv * nodep)311     virtual void visit(AstDiv* nodep) override { visit_add_sub_replace(nodep, true); }
visit(AstMul * nodep)312     virtual void visit(AstMul* nodep) override { visit_add_sub_replace(nodep, true); }
313     // These can't promote to real
visit(AstModDiv * nodep)314     virtual void visit(AstModDiv* nodep) override { visit_add_sub_replace(nodep, false); }
visit(AstModDivS * nodep)315     virtual void visit(AstModDivS* nodep) override { visit_add_sub_replace(nodep, false); }
visit(AstMulS * nodep)316     virtual void visit(AstMulS* nodep) override { visit_add_sub_replace(nodep, false); }
visit(AstDivS * nodep)317     virtual void visit(AstDivS* nodep) override { visit_add_sub_replace(nodep, false); }
318     // Widths: out width = lhs width, but upper matters
319     // Signed: Output signed iff LHS signed; unary operator
320     // Unary promote to real
visit(AstNegate * nodep)321     virtual void visit(AstNegate* nodep) override { visit_negate_not(nodep, true); }
322     // Unary never real
visit(AstNot * nodep)323     virtual void visit(AstNot* nodep) override { visit_negate_not(nodep, false); }
324 
325     // Real: inputs and output real
visit(AstAddD * nodep)326     virtual void visit(AstAddD* nodep) override { visit_real_add_sub(nodep); }
visit(AstSubD * nodep)327     virtual void visit(AstSubD* nodep) override { visit_real_add_sub(nodep); }
visit(AstDivD * nodep)328     virtual void visit(AstDivD* nodep) override { visit_real_add_sub(nodep); }
visit(AstMulD * nodep)329     virtual void visit(AstMulD* nodep) override { visit_real_add_sub(nodep); }
visit(AstPowD * nodep)330     virtual void visit(AstPowD* nodep) override { visit_real_add_sub(nodep); }
visit(AstNodeSystemBiop * nodep)331     virtual void visit(AstNodeSystemBiop* nodep) override { visit_real_add_sub(nodep); }
332     // Real: Output real
visit(AstNegateD * nodep)333     virtual void visit(AstNegateD* nodep) override { visit_real_neg_ceil(nodep); }
visit(AstNodeSystemUniop * nodep)334     virtual void visit(AstNodeSystemUniop* nodep) override { visit_real_neg_ceil(nodep); }
335 
336     // Widths: out signed/unsigned width = lhs width, input un|signed
visit(AstSigned * nodep)337     virtual void visit(AstSigned* nodep) override {
338         visit_signed_unsigned(nodep, VSigning::SIGNED);
339     }
visit(AstUnsigned * nodep)340     virtual void visit(AstUnsigned* nodep) override {
341         visit_signed_unsigned(nodep, VSigning::UNSIGNED);
342     }
343 
344     // Widths: Output width from lhs, rhs<33 bits
345     // Signed: If lhs signed
visit(AstShiftL * nodep)346     virtual void visit(AstShiftL* nodep) override { visit_shift(nodep); }
visit(AstShiftR * nodep)347     virtual void visit(AstShiftR* nodep) override { visit_shift(nodep); }
348     // ShiftRS converts to ShiftR, but not vice-versa
visit(AstShiftRS * nodep)349     virtual void visit(AstShiftRS* nodep) override { visit_shift(nodep); }
350 
351     //========
352     // Widths: Output real, input integer signed
visit(AstBitsToRealD * nodep)353     virtual void visit(AstBitsToRealD* nodep) override { visit_Or_Lu64(nodep); }
354 
355     // Widths: Output integer signed, input real
visit(AstRToIS * nodep)356     virtual void visit(AstRToIS* nodep) override { visit_Os32_Lr(nodep); }
visit(AstRToIRoundS * nodep)357     virtual void visit(AstRToIRoundS* nodep) override {
358         // Only created here, size comes from upper expression
359         if (m_vup->prelim()) {  // First stage evaluation
360             iterateCheckReal(nodep, "LHS", nodep->lhsp(), BOTH);
361         }
362         if (!nodep->dtypep()->widthSized()) nodep->v3fatalSrc("RToIRoundS should be presized");
363     }
364 
365     // Widths: Output integer unsigned, input real
visit(AstRealToBits * nodep)366     virtual void visit(AstRealToBits* nodep) override { visit_Ou64_Lr(nodep); }
367 
368     // Output integer, input string
visit(AstLenN * nodep)369     virtual void visit(AstLenN* nodep) override { visit_Os32_string(nodep); }
visit(AstPutcN * nodep)370     virtual void visit(AstPutcN* nodep) override {
371         // CALLER: str.putc()
372         UASSERT_OBJ(nodep->rhsp() && nodep->thsp(), nodep, "For ternary ops only!");
373         if (m_vup && m_vup->prelim()) {
374             // See similar handling in visit_cmp_eq_gt where created
375             iterateCheckString(nodep, "LHS", nodep->lhsp(), BOTH);
376             iterateCheckSigned32(nodep, "RHS", nodep->rhsp(), BOTH);
377             iterateCheckSigned32(nodep, "THS", nodep->thsp(), BOTH);
378             nodep->dtypeSetString();  // AstPutcN returns the new string to be assigned by
379                                       // AstAssign
380         }
381     }
visit(AstGetcN * nodep)382     virtual void visit(AstGetcN* nodep) override {
383         // CALLER: str.getc()
384         UASSERT_OBJ(nodep->rhsp(), nodep, "For binary ops only!");
385         if (m_vup && m_vup->prelim()) {
386             // See similar handling in visit_cmp_eq_gt where created
387             iterateCheckString(nodep, "LHS", nodep->lhsp(), BOTH);
388             iterateCheckSigned32(nodep, "RHS", nodep->rhsp(), BOTH);
389             nodep->dtypeSetBitSized(8, VSigning::UNSIGNED);
390         }
391     }
visit(AstGetcRefN * nodep)392     virtual void visit(AstGetcRefN* nodep) override {
393         // CALLER: str.getc()
394         UASSERT_OBJ(nodep->rhsp(), nodep, "For binary ops only!");
395         if (m_vup && m_vup->prelim()) {
396             // See similar handling in visit_cmp_eq_gt where created
397             iterateCheckString(nodep, "LHS", nodep->lhsp(), BOTH);
398             iterateCheckSigned32(nodep, "RHS", nodep->rhsp(), BOTH);
399             nodep->dtypeSetBitSized(8, VSigning::UNSIGNED);
400         }
401     }
visit(AstSubstrN * nodep)402     virtual void visit(AstSubstrN* nodep) override {
403         // CALLER: str.substr()
404         UASSERT_OBJ(nodep->rhsp() && nodep->thsp(), nodep, "For ternary ops only!");
405         if (m_vup && m_vup->prelim()) {
406             // See similar handling in visit_cmp_eq_gt where created
407             iterateCheckString(nodep, "LHS", nodep->lhsp(), BOTH);
408             iterateCheckSigned32(nodep, "RHS", nodep->rhsp(), BOTH);
409             iterateCheckSigned32(nodep, "THS", nodep->thsp(), BOTH);
410             nodep->dtypeSetString();
411         }
412     }
visit(AstCompareNN * nodep)413     virtual void visit(AstCompareNN* nodep) override {
414         // CALLER: str.compare(), str.icompare()
415         // Widths: 32 bit out
416         UASSERT_OBJ(nodep->rhsp(), nodep, "For binary ops only!");
417         if (m_vup->prelim()) {
418             // See similar handling in visit_cmp_eq_gt where created
419             iterateCheckString(nodep, "LHS", nodep->lhsp(), BOTH);
420             iterateCheckString(nodep, "RHS", nodep->rhsp(), BOTH);
421             nodep->dtypeSetSigned32();
422         }
423     }
visit(AstAtoN * nodep)424     virtual void visit(AstAtoN* nodep) override {
425         // CALLER: str.atobin(), atoi(), atohex(), atooct(), atoreal()
426         // Width: 64bit floating point for atoreal(), 32bit out for the others
427         if (m_vup->prelim()) {
428             // See similar handling in visit_cmp_eq_gt where created
429             iterateCheckString(nodep, "LHS", nodep->lhsp(), BOTH);
430             if (nodep->format() == AstAtoN::ATOREAL) {
431                 nodep->dtypeSetDouble();
432             } else {
433                 nodep->dtypeSetSigned32();
434             }
435         }
436     }
437 
438     // Widths: Constant, terminal
visit(AstTime * nodep)439     virtual void visit(AstTime* nodep) override { nodep->dtypeSetUInt64(); }
visit(AstTimeD * nodep)440     virtual void visit(AstTimeD* nodep) override { nodep->dtypeSetDouble(); }
visit(AstTestPlusArgs * nodep)441     virtual void visit(AstTestPlusArgs* nodep) override { nodep->dtypeSetSigned32(); }
visit(AstScopeName * nodep)442     virtual void visit(AstScopeName* nodep) override {
443         nodep->dtypeSetUInt64();  // A pointer, but not that it matters
444     }
445 
visit(AstNodeCond * nodep)446     virtual void visit(AstNodeCond* nodep) override {
447         // op = cond ? expr1 : expr2
448         // See IEEE-2012 11.4.11 and Table 11-21.
449         //   LHS is self-determined
450         //   Width: max(RHS, THS)
451         //   Signed: Output signed iff RHS & THS signed  (presumed, not in IEEE)
452         //   Real: Output real if either expression is real, non-real argument gets converted
453         if (m_vup->prelim()) {  // First stage evaluation
454             // Just once, do the conditional, expect one bit out.
455             iterateCheckBool(nodep, "Conditional Test", nodep->condp(), BOTH);
456             // Determine sub expression widths only relying on what's in the subops
457             //  CONTEXT determined, but need data type for pattern assignments
458             userIterateAndNext(nodep->expr1p(), WidthVP(m_vup->dtypeNullp(), PRELIM).p());
459             userIterateAndNext(nodep->expr2p(), WidthVP(m_vup->dtypeNullp(), PRELIM).p());
460             // Calculate width of this expression.
461             // First call (prelim()) m_vup->width() is probably zero, so we'll return
462             //  the size of this subexpression only.
463             // Second call (final()) m_vup->width() is probably the expression size, so
464             //  the expression includes the size of the output too.
465             if (nodep->expr1p()->isDouble() || nodep->expr2p()->isDouble()) {
466                 nodep->dtypeSetDouble();
467             } else if (nodep->expr1p()->isString() || nodep->expr2p()->isString()) {
468                 nodep->dtypeSetString();
469             } else {
470                 const int width = std::max(nodep->expr1p()->width(), nodep->expr2p()->width());
471                 const int mwidth
472                     = std::max(nodep->expr1p()->widthMin(), nodep->expr2p()->widthMin());
473                 const bool issigned = nodep->expr1p()->isSigned() && nodep->expr2p()->isSigned();
474                 nodep->dtypeSetLogicUnsized(width, mwidth, VSigning::fromBool(issigned));
475             }
476         }
477         if (m_vup->final()) {
478             AstNodeDType* const expDTypep = m_vup->dtypeOverridep(nodep->dtypep());
479             AstNodeDType* const subDTypep = expDTypep;
480             nodep->dtypeFrom(expDTypep);
481             // Error report and change sizes for suboperands of this node.
482             iterateCheck(nodep, "Conditional True", nodep->expr1p(), CONTEXT, FINAL, subDTypep,
483                          EXTEND_EXP);
484             iterateCheck(nodep, "Conditional False", nodep->expr2p(), CONTEXT, FINAL, subDTypep,
485                          EXTEND_EXP);
486         }
487     }
visit(AstConcat * nodep)488     virtual void visit(AstConcat* nodep) override {
489         // Real: Not allowed (assumed)
490         // Signed: unsigned output, input either (assumed)
491         // IEEE-2012 Table 11-21, and 11.8.1:
492         //   LHS, RHS is self-determined
493         //   signed: Unsigned  (11.8.1)
494         //   width: LHS + RHS
495         AstNodeDType* const vdtypep = m_vup->dtypeNullSkipRefp();
496         userIterate(vdtypep, WidthVP(SELF, BOTH).p());
497         if (VN_IS(vdtypep, QueueDType)) {
498             // Queue "element 0" is lhsp, so we need to swap arguments
499             auto* const newp = new AstConsQueue(nodep->fileline(), nodep->rhsp()->unlinkFrBack(),
500                                                 nodep->lhsp()->unlinkFrBack());
501             nodep->replaceWith(newp);
502             VL_DO_DANGLING(pushDeletep(nodep), nodep);
503             userIterateChildren(newp, m_vup);
504             return;
505         }
506         if (VN_IS(vdtypep, DynArrayDType)) {
507             auto* const newp = new AstConsDynArray(
508                 nodep->fileline(), nodep->rhsp()->unlinkFrBack(), nodep->lhsp()->unlinkFrBack());
509             nodep->replaceWith(newp);
510             VL_DO_DANGLING(pushDeletep(nodep), nodep);
511             userIterateChildren(newp, m_vup);
512             return;
513         }
514         if (m_vup->prelim()) {
515             if (VN_IS(vdtypep, AssocArrayDType)  //
516                 || VN_IS(vdtypep, DynArrayDType)  //
517                 || VN_IS(vdtypep, QueueDType)) {
518                 nodep->v3warn(E_UNSUPPORTED, "Unsupported: Concatenation to form "
519                                                  << vdtypep->prettyDTypeNameQ() << "data type");
520             }
521 
522             iterateCheckSizedSelf(nodep, "LHS", nodep->lhsp(), SELF, BOTH);
523             iterateCheckSizedSelf(nodep, "RHS", nodep->rhsp(), SELF, BOTH);
524             nodep->dtypeSetLogicUnsized(nodep->lhsp()->width() + nodep->rhsp()->width(),
525                                         nodep->lhsp()->widthMin() + nodep->rhsp()->widthMin(),
526                                         VSigning::UNSIGNED);
527             // Cleanup zero width Verilog2001 {x,{0{foo}}} now,
528             // otherwise having width(0) will cause later assertions to fire
529             if (const AstReplicate* const repp = VN_CAST(nodep->lhsp(), Replicate)) {
530                 if (repp->width() == 0) {  // Keep rhs
531                     nodep->replaceWith(nodep->rhsp()->unlinkFrBack());
532                     VL_DO_DANGLING(pushDeletep(nodep), nodep);
533                     return;
534                 }
535             }
536             if (const AstReplicate* const repp = VN_CAST(nodep->rhsp(), Replicate)) {
537                 if (repp->width() == 0) {  // Keep lhs
538                     nodep->replaceWith(nodep->lhsp()->unlinkFrBack());
539                     VL_DO_DANGLING(pushDeletep(nodep), nodep);
540                     return;
541                 }
542             }
543         }
544         if (m_vup->final()) {
545             if (nodep->lhsp()->isString() || nodep->rhsp()->isString()) {
546                 AstNode* const newp
547                     = new AstConcatN(nodep->fileline(), nodep->lhsp()->unlinkFrBack(),
548                                      nodep->rhsp()->unlinkFrBack());
549                 nodep->replaceWith(newp);
550                 VL_DO_DANGLING(pushDeletep(nodep), nodep);
551                 return;
552             }
553             if (!nodep->dtypep()->widthSized()) {
554                 // See also error in V3Number
555                 nodeForUnsizedWarning(nodep)->v3warn(
556                     WIDTHCONCAT, "Unsized numbers/parameters not allowed in concatenations.");
557             }
558         }
559     }
visit(AstConcatN * nodep)560     virtual void visit(AstConcatN* nodep) override {
561         // String concatenate.
562         // Already did AstConcat simplifications
563         if (m_vup->prelim()) {
564             iterateCheckString(nodep, "LHS", nodep->lhsp(), BOTH);
565             iterateCheckString(nodep, "RHS", nodep->rhsp(), BOTH);
566             nodep->dtypeSetString();
567         }
568         if (m_vup->final()) {
569             if (!nodep->dtypep()->widthSized()) {
570                 // See also error in V3Number
571                 nodeForUnsizedWarning(nodep)->v3warn(
572                     WIDTHCONCAT, "Unsized numbers/parameters not allowed in concatenations.");
573             }
574         }
575     }
visit(AstDelay * nodep)576     virtual void visit(AstDelay* nodep) override {
577         if (VN_IS(m_procedurep, Final)) {
578             nodep->v3error("Delays are not legal in final blocks (IEEE 1800-2017 9.2.3)");
579             VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
580             return;
581         }
582         if (VN_IS(m_ftaskp, Func)) {
583             nodep->v3error("Delays are not legal in functions. Suggest use a task "
584                            "(IEEE 1800-2017 13.4.4)");
585             VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
586             return;
587         }
588         nodep->v3warn(STMTDLY, "Unsupported: Ignoring delay on this delayed statement.");
589         VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
590     }
visit(AstFork * nodep)591     virtual void visit(AstFork* nodep) override {
592         if (VN_IS(m_ftaskp, Func) && !nodep->joinType().joinNone()) {
593             nodep->v3error("Only fork .. join_none is legal in functions. "
594                            "(IEEE 1800-2017 13.4.4)");
595             VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
596             return;
597         }
598         if (v3Global.opt.bboxUnsup()
599             // With no statements, begin is identical
600             || !nodep->stmtsp()
601             // With one statement, a begin block does as good as a fork/join or join_any
602             || (!nodep->stmtsp()->nextp() && !nodep->joinType().joinNone())) {
603             AstNode* stmtsp = nullptr;
604             if (nodep->stmtsp()) stmtsp = nodep->stmtsp()->unlinkFrBack();
605             AstBegin* const newp = new AstBegin{nodep->fileline(), nodep->name(), stmtsp};
606             nodep->replaceWith(newp);
607             VL_DO_DANGLING(nodep->deleteTree(), nodep);
608         } else {
609             nodep->v3warn(E_UNSUPPORTED, "Unsupported: fork statements");
610             // TBD might support only normal join, if so complain about other join flavors
611         }
612     }
visit(AstDisableFork * nodep)613     virtual void visit(AstDisableFork* nodep) override {
614         nodep->v3warn(E_UNSUPPORTED, "Unsupported: disable fork statements");
615         VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
616     }
visit(AstWaitFork * nodep)617     virtual void visit(AstWaitFork* nodep) override {
618         nodep->v3warn(E_UNSUPPORTED, "Unsupported: wait fork statements");
619         VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
620     }
visit(AstToLowerN * nodep)621     virtual void visit(AstToLowerN* nodep) override {
622         if (m_vup->prelim()) {
623             iterateCheckString(nodep, "LHS", nodep->lhsp(), BOTH);
624             nodep->dtypeSetString();
625         }
626     }
visit(AstToUpperN * nodep)627     virtual void visit(AstToUpperN* nodep) override {
628         if (m_vup->prelim()) {
629             iterateCheckString(nodep, "LHS", nodep->lhsp(), BOTH);
630             nodep->dtypeSetString();
631         }
632     }
visit(AstReplicate * nodep)633     virtual void visit(AstReplicate* nodep) override {
634         // IEEE-2012 Table 11-21:
635         //   LHS, RHS is self-determined
636         //   width: value(LHS) * width(RHS)
637         if (m_vup->prelim()) {
638             iterateCheckSizedSelf(nodep, "RHS", nodep->rhsp(), SELF, BOTH);
639             V3Const::constifyParamsEdit(nodep->rhsp());  // rhsp may change
640             const AstConst* const constp = VN_CAST(nodep->rhsp(), Const);
641             if (!constp) {
642                 nodep->v3error("Replication value isn't a constant.");
643                 return;
644             }
645             uint32_t times = constp->toUInt();
646             if (times == 0
647                 && !VN_IS(nodep->backp(), Concat)) {  // Concat Visitor will clean it up.
648                 nodep->v3error("Replication value of 0 is only legal under a concatenation (IEEE "
649                                "1800-2017 11.4.12.1)");
650                 times = 1;
651             }
652 
653             AstNodeDType* const vdtypep = m_vup->dtypeNullSkipRefp();
654             if (VN_IS(vdtypep, QueueDType) || VN_IS(vdtypep, DynArrayDType)) {
655                 if (times != 1)
656                     nodep->v3warn(E_UNSUPPORTED, "Unsupported: Non-1 replication to form "
657                                                      << vdtypep->prettyDTypeNameQ()
658                                                      << " data type");
659                 // Don't iterate lhsp as SELF, the potential Concat below needs
660                 // the adtypep passed down to recognize the QueueDType
661                 userIterateAndNext(nodep->lhsp(), WidthVP(vdtypep, BOTH).p());
662                 nodep->replaceWith(nodep->lhsp()->unlinkFrBack());
663                 VL_DO_DANGLING(pushDeletep(nodep), nodep);
664                 return;
665             }
666             if (VN_IS(vdtypep, AssocArrayDType) || VN_IS(vdtypep, UnpackArrayDType)) {
667                 nodep->v3warn(E_UNSUPPORTED, "Unsupported: Replication to form "
668                                                  << vdtypep->prettyDTypeNameQ() << " data type");
669             }
670             iterateCheckSizedSelf(nodep, "LHS", nodep->lhsp(), SELF, BOTH);
671             if (nodep->lhsp()->isString()) {
672                 AstNode* const newp
673                     = new AstReplicateN(nodep->fileline(), nodep->lhsp()->unlinkFrBack(),
674                                         nodep->rhsp()->unlinkFrBack());
675                 nodep->replaceWith(newp);
676                 VL_DO_DANGLING(pushDeletep(nodep), nodep);
677                 return;
678             } else {
679                 nodep->dtypeSetLogicUnsized((nodep->lhsp()->width() * times),
680                                             (nodep->lhsp()->widthMin() * times),
681                                             VSigning::UNSIGNED);
682             }
683         }
684         if (m_vup->final()) {
685             if (!nodep->dtypep()->widthSized()) {
686                 // See also error in V3Number
687                 nodeForUnsizedWarning(nodep)->v3warn(
688                     WIDTHCONCAT, "Unsized numbers/parameters not allowed in replications.");
689             }
690         }
691     }
visit(AstReplicateN * nodep)692     virtual void visit(AstReplicateN* nodep) override {
693         // Replicate with string
694         if (m_vup->prelim()) {
695             iterateCheckString(nodep, "LHS", nodep->lhsp(), BOTH);
696             iterateCheckSizedSelf(nodep, "RHS", nodep->rhsp(), SELF, BOTH);
697             V3Const::constifyParamsEdit(nodep->rhsp());  // rhsp may change
698             const AstConst* const constp = VN_CAST(nodep->rhsp(), Const);
699             if (!constp) {
700                 nodep->v3error("Replication value isn't a constant.");
701                 return;
702             }
703             const uint32_t times = constp->toUInt();
704             if (times == 0
705                 && !VN_IS(nodep->backp(), Concat)) {  // Concat Visitor will clean it up.
706                 nodep->v3error("Replication value of 0 is only legal under a concatenation (IEEE "
707                                "1800-2017 11.4.12.1)");
708             }
709             nodep->dtypeSetString();
710         }
711         if (m_vup->final()) {
712             if (!nodep->dtypep()->widthSized()) {
713                 // See also error in V3Number
714                 nodeForUnsizedWarning(nodep)->v3warn(
715                     WIDTHCONCAT, "Unsized numbers/parameters not allowed in replications.");
716             }
717         }
718     }
visit(AstNodeStream * nodep)719     virtual void visit(AstNodeStream* nodep) override {
720         if (m_vup->prelim()) {
721             iterateCheckSizedSelf(nodep, "LHS", nodep->lhsp(), SELF, BOTH);
722             iterateCheckSizedSelf(nodep, "RHS", nodep->rhsp(), SELF, BOTH);
723             V3Const::constifyParamsEdit(nodep->rhsp());  // rhsp may change
724             const AstConst* const constp = VN_CAST(nodep->rhsp(), Const);
725             AstBasicDType* const basicp = VN_CAST(nodep->rhsp(), BasicDType);
726             if (!constp && !basicp) {
727                 nodep->v3error("Slice size isn't a constant or basic data type.");
728                 return;
729             }
730             if (basicp) {  // Convert data type to a constant size
731                 AstConst* const newp = new AstConst(basicp->fileline(), basicp->width());
732                 nodep->rhsp()->replaceWith(newp);
733                 pushDeletep(basicp);
734             } else {
735                 const uint32_t sliceSize = constp->toUInt();
736                 if (!sliceSize) {
737                     nodep->v3error("Slice size cannot be zero.");
738                     return;
739                 }
740             }
741             nodep->dtypeSetLogicUnsized(nodep->lhsp()->width(), nodep->lhsp()->widthMin(),
742                                         VSigning::UNSIGNED);
743         }
744         if (m_vup->final()) {
745             if (!nodep->dtypep()->widthSized()) {
746                 // See also error in V3Number
747                 nodeForUnsizedWarning(nodep)->v3warn(
748                     WIDTHCONCAT, "Unsized numbers/parameters not allowed in streams.");
749             }
750         }
751     }
visit(AstRange * nodep)752     virtual void visit(AstRange* nodep) override {
753         // Real: Not allowed
754         // Signed: unsigned output, input either
755         // Convert all range values to constants
756         UINFO(6, "RANGE " << nodep << endl);
757         V3Const::constifyParamsEdit(nodep->leftp());  // May relink pointed to node
758         V3Const::constifyParamsEdit(nodep->rightp());  // May relink pointed to node
759         checkConstantOrReplace(nodep->leftp(), "left side of bit range isn't a constant");
760         checkConstantOrReplace(nodep->rightp(), "right side of bit range isn't a constant");
761         if (m_vup->prelim()) {
762             // Don't need to iterate because V3Const already constified
763             const int width = nodep->elementsConst();
764             if (width > (1 << 28)) {
765                 nodep->v3error("Width of bit range is huge; vector of over 1billion bits: 0x"
766                                << std::hex << width);
767             }
768             // Note width() not set on range; use elementsConst()
769             if (nodep->littleEndian() && !VN_IS(nodep->backp(), UnpackArrayDType)
770                 && !VN_IS(nodep->backp(), Cell)) {  // For cells we warn in V3Inst
771                 nodep->v3warn(LITENDIAN, "Little bit endian vector: left < right of bit range: ["
772                                              << nodep->leftConst() << ":" << nodep->rightConst()
773                                              << "]");
774             }
775         }
776     }
777 
visit(AstSel * nodep)778     virtual void visit(AstSel* nodep) override {
779         // Signed: always unsigned; Real: Not allowed
780         // LSB is self-determined (IEEE 2012 11.5.1)
781         // We also use SELs to shorten a signed constant etc, in this case they are signed.
782         if (nodep->didWidth()) return;
783         UASSERT_OBJ(m_vup, nodep, "Select under an unexpected context");
784         if (m_vup->prelim()) {
785             if (debug() >= 9) nodep->dumpTree(cout, "-selWidth: ");
786             userIterateAndNext(nodep->fromp(), WidthVP(CONTEXT, PRELIM).p());
787             userIterateAndNext(nodep->lsbp(), WidthVP(SELF, PRELIM).p());
788             checkCvtUS(nodep->fromp());
789             iterateCheckSizedSelf(nodep, "Select Width", nodep->widthp(), SELF, BOTH);
790             iterateCheckSizedSelf(nodep, "Select LHS", nodep->lhsp(), SELF, BOTH);
791             V3Const::constifyParamsEdit(nodep->widthp());  // widthp may change
792             const AstConst* const widthConstp = VN_CAST(nodep->widthp(), Const);
793             if (!widthConstp) {
794                 nodep->v3error("Width of bit extract isn't a constant");
795                 nodep->dtypeSetBit();
796                 return;
797             }
798             int width = nodep->widthConst();
799             UASSERT_OBJ(nodep->dtypep(), nodep, "dtype wasn't set");  // by V3WidthSel
800             if (VN_IS(nodep->lsbp(), Const) && nodep->msbConst() < nodep->lsbConst()) {
801                 nodep->v3warn(E_UNSUPPORTED, "Unsupported: left < right of bit extract: "
802                                                  << nodep->msbConst() << "<" << nodep->lsbConst());
803                 width = (nodep->lsbConst() - nodep->msbConst() + 1);
804                 nodep->dtypeSetLogicSized(width, VSigning::UNSIGNED);
805                 nodep->widthp()->replaceWith(new AstConst(nodep->widthp()->fileline(), width));
806                 nodep->lsbp()->replaceWith(new AstConst(nodep->lsbp()->fileline(), 0));
807             }
808             // We're extracting, so just make sure the expression is at least wide enough.
809             if (nodep->fromp()->width() < width) {
810                 nodep->v3warn(SELRANGE, "Extracting " << width << " bits from only "
811                                                       << nodep->fromp()->width() << " bit number");
812                 // Extend it.
813                 AstNodeDType* const subDTypep
814                     = nodep->findLogicDType(width, width, nodep->fromp()->dtypep()->numeric());
815                 widthCheckSized(nodep, "errorless...", nodep->fromp(), subDTypep, EXTEND_EXP,
816                                 false /*noerror*/);
817             }
818             // Check bit indexes.
819             // What is the MSB?  We want the true MSB, not one starting at
820             // 0, because a 4 bit index is required to look at a one-bit
821             // variable[15:15] and 5 bits for [15:-2]
822             int frommsb = nodep->fromp()->width() - 1;
823             int fromlsb = 0;
824             const int elw = nodep->declElWidth();  // Must adjust to tell user bit ranges
825             if (nodep->declRange().ranged()) {
826                 frommsb = nodep->declRange().hiMaxSelect() * elw
827                           + (elw - 1);  // Corrected for negative lsb
828                 fromlsb = nodep->declRange().lo() * elw;
829             } else {
830                 // nodep->v3fatalSrc("Should have been declRanged in V3WidthSel");
831             }
832             const int selwidth = V3Number::log2b(frommsb + 1 - 1) + 1;  // Width to address a bit
833             AstNodeDType* const selwidthDTypep
834                 = nodep->findLogicDType(selwidth, selwidth, nodep->lsbp()->dtypep()->numeric());
835             userIterateAndNext(nodep->fromp(), WidthVP(SELF, FINAL).p());
836             userIterateAndNext(nodep->lsbp(), WidthVP(SELF, FINAL).p());
837             if (widthBad(nodep->lsbp(), selwidthDTypep) && nodep->lsbp()->width() != 32) {
838                 if (!nodep->fileline()->warnIsOff(V3ErrorCode::WIDTH)) {
839                     nodep->v3warn(WIDTH,
840                                   "Bit extraction of var["
841                                       << (frommsb / elw) << ":" << (fromlsb / elw) << "] requires "
842                                       << (selwidth / elw) << " bit index, not "
843                                       << (nodep->lsbp()->width() / elw)
844                                       << (nodep->lsbp()->width() != nodep->lsbp()->widthMin()
845                                               ? " or " + cvtToStr(nodep->lsbp()->widthMin() / elw)
846                                               : "")
847                                       << " bits.");
848                     UINFO(1, "    Related node: " << nodep << endl);
849                 }
850             }
851             if (VN_IS(nodep->lsbp(), Const) && nodep->msbConst() > frommsb) {
852                 // See also warning in V3Const
853                 // We need to check here, because the widthCheckSized may silently
854                 // add another SEL which will lose the out-of-range check
855                 //
856                 // We don't want to trigger an error here if we are just
857                 // evaluating type sizes for a generate block condition. We
858                 // should only trigger the error if the out-of-range access is
859                 // actually generated.
860                 if (m_doGenerate) {
861                     UINFO(5, "Selection index out of range inside generate." << endl);
862                 } else {
863                     nodep->v3warn(SELRANGE, "Selection index out of range: "
864                                                 << nodep->msbConst() << ":" << nodep->lsbConst()
865                                                 << " outside " << frommsb << ":" << fromlsb);
866                     UINFO(1, "    Related node: " << nodep << endl);
867                 }
868             }
869             // iterate FINAL is two blocks above
870             //
871             // If we have a width problem with GENERATE etc, this will reduce
872             // it down and mask it, so we have no chance of finding a real
873             // error in the future. So don't do this for them.
874             if (!m_doGenerate) {
875                 // lsbp() must be self-determined, however for performance
876                 // we want the select to be truncated to fit within the
877                 // maximum select range, e.g. turn Xs outside of the select
878                 // into something fast which pulls from within the array.
879                 widthCheckSized(nodep, "Extract Range", nodep->lsbp(), selwidthDTypep, EXTEND_EXP,
880                                 false /*NOWARN*/);
881             }
882         }
883     }
884 
visit(AstArraySel * nodep)885     virtual void visit(AstArraySel* nodep) override {
886         // Signed/Real: Output signed iff LHS signed/real; binary operator
887         // Note by contrast, bit extract selects are unsigned
888         // LSB is self-determined (IEEE 2012 11.5.1)
889         if (m_vup->prelim()) {
890             iterateCheckSizedSelf(nodep, "Bit select", nodep->bitp(), SELF, BOTH);
891             userIterateAndNext(nodep->fromp(), WidthVP(SELF, BOTH).p());
892             //
893             int frommsb;
894             int fromlsb;
895             const AstNodeDType* const fromDtp = nodep->fromp()->dtypep()->skipRefp();
896             if (const AstUnpackArrayDType* const adtypep = VN_CAST(fromDtp, UnpackArrayDType)) {
897                 frommsb = adtypep->hi();
898                 fromlsb = adtypep->lo();
899                 if (fromlsb > frommsb) {
900                     const int t = frommsb;
901                     frommsb = fromlsb;
902                     fromlsb = t;
903                 }
904                 // However, if the lsb<0 we may go negative, so need more bits!
905                 if (fromlsb < 0) frommsb += -fromlsb;
906                 nodep->dtypeFrom(adtypep->subDTypep());  // Need to strip off array reference
907             } else {
908                 // Note PackArrayDType doesn't use an ArraySel but a normal Sel.
909                 UINFO(1, "    Related dtype: " << fromDtp << endl);
910                 nodep->v3fatalSrc("Array reference exceeds dimension of array");
911                 frommsb = fromlsb = 0;
912             }
913             const int selwidth = V3Number::log2b(frommsb + 1 - 1) + 1;  // Width to address a bit
914             AstNodeDType* const selwidthDTypep
915                 = nodep->findLogicDType(selwidth, selwidth, nodep->bitp()->dtypep()->numeric());
916             if (widthBad(nodep->bitp(), selwidthDTypep) && nodep->bitp()->width() != 32) {
917                 nodep->v3warn(WIDTH, "Bit extraction of array["
918                                          << frommsb << ":" << fromlsb << "] requires " << selwidth
919                                          << " bit index, not " << nodep->bitp()->width()
920                                          << (nodep->bitp()->width() != nodep->bitp()->widthMin()
921                                                  ? " or " + cvtToStr(nodep->bitp()->widthMin())
922                                                  : "")
923                                          << " bits.");
924                 if (!nodep->fileline()->warnIsOff(V3ErrorCode::WIDTH)) {
925                     UINFO(1, "    Related node: " << nodep << endl);
926                     UINFO(1, "    Related dtype: " << nodep->dtypep() << endl);
927                 }
928             }
929             if (!m_doGenerate) {
930                 // Must check bounds before adding a select that truncates the bound
931                 // Note we've already subtracted off LSB
932                 if (VN_IS(nodep->bitp(), Const)
933                     && (VN_AS(nodep->bitp(), Const)->toSInt() > (frommsb - fromlsb)
934                         || VN_AS(nodep->bitp(), Const)->toSInt() < 0)) {
935                     nodep->v3warn(SELRANGE,
936                                   "Selection index out of range: "
937                                       << (VN_AS(nodep->bitp(), Const)->toSInt() + fromlsb)
938                                       << " outside " << frommsb << ":" << fromlsb);
939                     UINFO(1, "    Related node: " << nodep << endl);
940                 }
941                 widthCheckSized(nodep, "Extract Range", nodep->bitp(), selwidthDTypep, EXTEND_EXP,
942                                 false /*NOWARN*/);
943             }
944         }
945     }
946 
visit(AstAssocSel * nodep)947     virtual void visit(AstAssocSel* nodep) override {
948         // Signed/Real: Output type based on array-declared type; binary operator
949         if (m_vup->prelim()) {
950             const AstNodeDType* const fromDtp = nodep->fromp()->dtypep()->skipRefp();
951             const AstAssocArrayDType* const adtypep = VN_CAST(fromDtp, AssocArrayDType);
952             if (!adtypep) {
953                 UINFO(1, "    Related dtype: " << fromDtp << endl);
954                 nodep->v3fatalSrc("Associative array reference is not to associative array");
955             }
956             iterateCheckTyped(nodep, "Associative select", nodep->bitp(), adtypep->keyDTypep(),
957                               BOTH);
958             nodep->dtypeFrom(adtypep->subDTypep());
959         }
960     }
961 
visit(AstSliceSel * nodep)962     virtual void visit(AstSliceSel* nodep) override {
963         // Always creates as output an unpacked array
964         if (m_vup->prelim()) {
965             userIterateAndNext(nodep->fromp(), WidthVP(SELF, BOTH).p());
966             //
967             // Array indices are always constant
968             const AstNodeDType* const fromDtp = nodep->fromp()->dtypep()->skipRefp();
969             const AstUnpackArrayDType* const adtypep = VN_CAST(fromDtp, UnpackArrayDType);
970             if (!adtypep) {
971                 UINFO(1, "    Related dtype: " << fromDtp << endl);
972                 nodep->v3fatalSrc("Packed array reference exceeds dimension of array");
973             }
974             // Build new array Dtype based on the original's base type, but with new bounds
975             AstNodeDType* const newDtp
976                 = new AstUnpackArrayDType(nodep->fileline(), adtypep->subDTypep(),
977                                           new AstRange(nodep->fileline(), nodep->declRange()));
978             v3Global.rootp()->typeTablep()->addTypesp(newDtp);
979             nodep->dtypeFrom(newDtp);
980 
981             if (!m_doGenerate) {
982                 // Must check bounds before adding a select that truncates the bound
983                 // Note we've already subtracted off LSB
984                 const int subtracted = adtypep->declRange().lo();
985                 // Add subtracted value to get the original range
986                 const VNumRange declRange{nodep->declRange().hi() + subtracted,
987                                           nodep->declRange().lo() + subtracted,
988                                           nodep->declRange().littleEndian()};
989                 if ((declRange.hi() > adtypep->declRange().hi())
990                     || declRange.lo() < adtypep->declRange().lo()) {
991                     // Other simulators warn too
992                     nodep->v3error("Slice selection index '" << declRange << "'"
993                                                              << " outside data type's '"
994                                                              << adtypep->declRange() << "'");
995                 } else if ((declRange.littleEndian() != adtypep->declRange().littleEndian())
996                            && declRange.hi() != declRange.lo()) {
997                     nodep->v3error("Slice selection '"
998                                    << declRange << "'"
999                                    << " has backward indexing versus data type's '"
1000                                    << adtypep->declRange() << "'");
1001                 }
1002             }
1003         }
1004     }
1005 
visit(AstSelBit * nodep)1006     virtual void visit(AstSelBit* nodep) override {
1007         // Just a quick check as after V3Param these nodes instead are AstSel's
1008         userIterateAndNext(nodep->fromp(), WidthVP(CONTEXT, PRELIM).p());  // FINAL in AstSel
1009         userIterateAndNext(nodep->rhsp(), WidthVP(CONTEXT, PRELIM).p());  // FINAL in AstSel
1010         userIterateAndNext(nodep->thsp(), WidthVP(CONTEXT, PRELIM).p());  // FINAL in AstSel
1011         userIterateAndNext(nodep->attrp(), WidthVP(SELF, BOTH).p());
1012         AstNode* const selp = V3Width::widthSelNoIterEdit(nodep);
1013         if (selp != nodep) {
1014             nodep = nullptr;
1015             userIterate(selp, m_vup);
1016             return;
1017         }
1018         nodep->v3fatalSrc("AstSelBit should disappear after widthSel");
1019     }
visit(AstSelExtract * nodep)1020     virtual void visit(AstSelExtract* nodep) override {
1021         // Just a quick check as after V3Param these nodes instead are AstSel's
1022         userIterateAndNext(nodep->fromp(), WidthVP(CONTEXT, PRELIM).p());  // FINAL in AstSel
1023         userIterateAndNext(nodep->rhsp(), WidthVP(CONTEXT, PRELIM).p());  // FINAL in AstSel
1024         userIterateAndNext(nodep->thsp(), WidthVP(CONTEXT, PRELIM).p());  // FINAL in AstSel
1025         userIterateAndNext(nodep->attrp(), WidthVP(SELF, BOTH).p());
1026         AstNode* const selp = V3Width::widthSelNoIterEdit(nodep);
1027         if (selp != nodep) {
1028             nodep = nullptr;
1029             userIterate(selp, m_vup);
1030             return;
1031         }
1032         nodep->v3fatalSrc("AstSelExtract should disappear after widthSel");
1033     }
visit(AstSelPlus * nodep)1034     virtual void visit(AstSelPlus* nodep) override {
1035         userIterateAndNext(nodep->fromp(), WidthVP(CONTEXT, PRELIM).p());  // FINAL in AstSel
1036         userIterateAndNext(nodep->rhsp(), WidthVP(CONTEXT, PRELIM).p());  // FINAL in AstSel
1037         userIterateAndNext(nodep->thsp(), WidthVP(CONTEXT, PRELIM).p());  // FINAL in AstSel
1038         userIterateAndNext(nodep->attrp(), WidthVP(SELF, BOTH).p());
1039         AstNode* const selp = V3Width::widthSelNoIterEdit(nodep);
1040         if (selp != nodep) {
1041             nodep = nullptr;
1042             userIterate(selp, m_vup);
1043             return;
1044         }
1045         nodep->v3fatalSrc("AstSelPlus should disappear after widthSel");
1046     }
visit(AstSelMinus * nodep)1047     virtual void visit(AstSelMinus* nodep) override {
1048         userIterateAndNext(nodep->fromp(), WidthVP(CONTEXT, PRELIM).p());  // FINAL in AstSel
1049         userIterateAndNext(nodep->rhsp(), WidthVP(CONTEXT, PRELIM).p());  // FINAL in AstSel
1050         userIterateAndNext(nodep->thsp(), WidthVP(CONTEXT, PRELIM).p());  // FINAL in AstSel
1051         userIterateAndNext(nodep->attrp(), WidthVP(SELF, BOTH).p());
1052         AstNode* const selp = V3Width::widthSelNoIterEdit(nodep);
1053         if (selp != nodep) {
1054             nodep = nullptr;
1055             userIterate(selp, m_vup);
1056             return;
1057         }
1058         nodep->v3fatalSrc("AstSelMinus should disappear after widthSel");
1059     }
1060 
visit(AstExtend * nodep)1061     virtual void visit(AstExtend* nodep) override {
1062         // Only created by this process, so we know width from here down is correct.
1063     }
visit(AstExtendS * nodep)1064     virtual void visit(AstExtendS* nodep) override {
1065         // Only created by this process, so we know width from here down is correct.
1066     }
visit(AstConst * nodep)1067     virtual void visit(AstConst* nodep) override {
1068         // The node got setup with the signed/real state of the node.
1069         // However a later operation may have changed the node->signed w/o changing
1070         // the number's sign.  So we don't: nodep->dtypeChgSigned(nodep->num().isSigned());
1071         if (m_vup && m_vup->prelim()) {
1072             if (nodep->num().isString()) {
1073                 nodep->dtypeSetString();
1074             } else if (nodep->num().sized()) {
1075                 nodep->dtypeChgWidth(nodep->num().width(), nodep->num().width());
1076             } else {
1077                 nodep->dtypeChgWidth(nodep->num().width(), nodep->num().widthMin());
1078             }
1079         }
1080         // We don't size the constant until we commit the widths, as need parameters
1081         // to remain unsized, and numbers to remain unsized to avoid backp() warnings
1082     }
visit(AstEmptyQueue * nodep)1083     virtual void visit(AstEmptyQueue* nodep) override {
1084         nodep->dtypeSetEmptyQueue();
1085         if (!VN_IS(nodep->backp(), Assign))
1086             nodep->v3warn(E_UNSUPPORTED,
1087                           "Unsupported/Illegal: empty queue ('{}') in this context");
1088     }
visit(AstFell * nodep)1089     virtual void visit(AstFell* nodep) override {
1090         if (m_vup->prelim()) {
1091             iterateCheckSizedSelf(nodep, "LHS", nodep->exprp(), SELF, BOTH);
1092             nodep->dtypeSetBit();
1093         }
1094     }
visit(AstPast * nodep)1095     virtual void visit(AstPast* nodep) override {
1096         if (m_vup->prelim()) {
1097             iterateCheckSizedSelf(nodep, "LHS", nodep->exprp(), SELF, BOTH);
1098             nodep->dtypeFrom(nodep->exprp());
1099             if (nodep->ticksp()) {
1100                 iterateCheckSizedSelf(nodep, "Ticks", nodep->ticksp(), SELF, BOTH);
1101                 V3Const::constifyParamsEdit(nodep->ticksp());  // ticksp may change
1102                 const AstConst* const constp = VN_CAST(nodep->ticksp(), Const);
1103                 if (!constp) {
1104                     nodep->v3error("$past tick value must be constant (IEEE 1800-2017 16.9.3)");
1105                     nodep->ticksp()->unlinkFrBack()->deleteTree();
1106                 } else if (constp->toSInt() < 1) {
1107                     constp->v3error("$past tick value must be >= 1 (IEEE 1800-2017 16.9.3)");
1108                     nodep->ticksp()->unlinkFrBack()->deleteTree();
1109                 } else {
1110                     if (constp->toSInt() > 10) {
1111                         constp->v3warn(TICKCOUNT, "$past tick value of "
1112                                                       << constp->toSInt()
1113                                                       << " may have a large performance cost");
1114                     }
1115                 }
1116             }
1117         }
1118     }
visit(AstRose * nodep)1119     virtual void visit(AstRose* nodep) override {
1120         if (m_vup->prelim()) {
1121             iterateCheckSizedSelf(nodep, "LHS", nodep->exprp(), SELF, BOTH);
1122             nodep->dtypeSetBit();
1123         }
1124     }
1125 
visit(AstSampled * nodep)1126     virtual void visit(AstSampled* nodep) override {
1127         if (m_vup->prelim()) {
1128             iterateCheckSizedSelf(nodep, "LHS", nodep->exprp(), SELF, BOTH);
1129             nodep->dtypeFrom(nodep->exprp());
1130         }
1131     }
1132 
visit(AstStable * nodep)1133     virtual void visit(AstStable* nodep) override {
1134         if (m_vup->prelim()) {
1135             iterateCheckSizedSelf(nodep, "LHS", nodep->exprp(), SELF, BOTH);
1136             nodep->dtypeSetBit();
1137         }
1138     }
1139 
visit(AstImplication * nodep)1140     virtual void visit(AstImplication* nodep) override {
1141         if (m_vup->prelim()) {
1142             iterateCheckBool(nodep, "LHS", nodep->lhsp(), BOTH);
1143             iterateCheckBool(nodep, "RHS", nodep->rhsp(), BOTH);
1144             nodep->dtypeSetBit();
1145         }
1146     }
1147 
visit(AstRand * nodep)1148     virtual void visit(AstRand* nodep) override {
1149         if (m_vup->prelim()) {
1150             if (nodep->urandom()) {
1151                 nodep->dtypeSetUInt32();  // Says the spec
1152             } else {
1153                 nodep->dtypeSetSigned32();  // Says the spec
1154             }
1155             if (nodep->seedp()) iterateCheckSigned32(nodep, "seed", nodep->seedp(), BOTH);
1156         }
1157     }
visit(AstURandomRange * nodep)1158     virtual void visit(AstURandomRange* nodep) override {
1159         if (m_vup->prelim()) {
1160             nodep->dtypeSetUInt32();  // Says the spec
1161             AstNodeDType* const expDTypep = nodep->findUInt32DType();
1162             userIterateAndNext(nodep->lhsp(), WidthVP(CONTEXT, PRELIM).p());
1163             userIterateAndNext(nodep->rhsp(), WidthVP(CONTEXT, PRELIM).p());
1164             iterateCheck(nodep, "LHS", nodep->lhsp(), SELF, FINAL, expDTypep, EXTEND_EXP);
1165             iterateCheck(nodep, "RHS", nodep->rhsp(), SELF, FINAL, expDTypep, EXTEND_EXP);
1166         }
1167     }
visit(AstUnbounded * nodep)1168     virtual void visit(AstUnbounded* nodep) override {
1169         nodep->dtypeSetSigned32();  // Used in int context
1170         if (VN_IS(nodep->backp(), IsUnbounded)) return;  // Ok, leave
1171         if (VN_IS(nodep->backp(), BracketArrayDType)) return;  // Ok, leave
1172         if (const auto* const varp = VN_CAST(nodep->backp(), Var)) {
1173             if (varp->isParam()) return;  // Ok, leave
1174         }
1175         // queue_slice[#:$]
1176         if (const auto* const selp = VN_CAST(nodep->backp(), SelExtract)) {
1177             if (VN_IS(selp->fromp()->dtypep(), QueueDType)) {
1178                 nodep->replaceWith(
1179                     new AstConst(nodep->fileline(), AstConst::Signed32(), 0x7FFFFFFF));
1180                 VL_DO_DANGLING(nodep->deleteTree(), nodep);
1181                 return;
1182             }
1183         }
1184         nodep->v3warn(E_UNSUPPORTED, "Unsupported/illegal unbounded ('$') in this context.");
1185     }
visit(AstIsUnbounded * nodep)1186     virtual void visit(AstIsUnbounded* nodep) override {
1187         if (m_vup->prelim()) {
1188             userIterateAndNext(nodep->lhsp(), WidthVP(SELF, BOTH).p());
1189             nodep->dtypeSetBit();
1190         }
1191     }
visit(AstUCFunc * nodep)1192     virtual void visit(AstUCFunc* nodep) override {
1193         // Give it the size the user wants.
1194         if (m_vup && m_vup->prelim()) {
1195             nodep->dtypeSetLogicUnsized(32, 1, VSigning::UNSIGNED);  // We don't care
1196             // All arguments seek their natural sizes
1197             userIterateChildren(nodep, WidthVP(SELF, BOTH).p());
1198         }
1199         if (m_vup->final()) {
1200             AstNodeDType* const expDTypep = m_vup->dtypeOverridep(nodep->dtypep());
1201             nodep->dtypeFrom(expDTypep);  // Assume user knows the rules; go with the flow
1202             if (nodep->width() > 64) {
1203                 nodep->v3warn(E_UNSUPPORTED, "Unsupported: $c can't generate wider than 64 bits");
1204             }
1205         }
1206     }
visit(AstCLog2 * nodep)1207     virtual void visit(AstCLog2* nodep) override {
1208         if (m_vup->prelim()) {
1209             iterateCheckSizedSelf(nodep, "LHS", nodep->lhsp(), SELF, BOTH);
1210             nodep->dtypeSetSigned32();
1211         }
1212     }
visit(AstPow * nodep)1213     virtual void visit(AstPow* nodep) override {
1214         // Pow is special, output sign only depends on LHS sign, but
1215         // function result depends on both signs
1216         // RHS is self-determined (IEEE)
1217         // Real if either side is real (as with AstAdd)
1218         if (m_vup->prelim()) {
1219             userIterateAndNext(nodep->lhsp(), WidthVP(CONTEXT, PRELIM).p());
1220             userIterateAndNext(nodep->rhsp(), WidthVP(CONTEXT, PRELIM).p());
1221             if (nodep->lhsp()->isDouble() || nodep->rhsp()->isDouble()) {
1222                 spliceCvtD(nodep->lhsp());
1223                 spliceCvtD(nodep->rhsp());
1224                 VL_DO_DANGLING(replaceWithDVersion(nodep), nodep);
1225                 return;
1226             }
1227 
1228             checkCvtUS(nodep->lhsp());
1229             iterateCheckSizedSelf(nodep, "RHS", nodep->rhsp(), SELF, BOTH);
1230             nodep->dtypeFrom(nodep->lhsp());
1231         }
1232 
1233         if (m_vup->final()) {
1234             AstNodeDType* const expDTypep = m_vup->dtypeOverridep(nodep->dtypep());
1235             nodep->dtypeFrom(expDTypep);
1236             // rhs already finalized in iterate_shift_prelim
1237             iterateCheck(nodep, "LHS", nodep->lhsp(), SELF, FINAL, nodep->dtypep(), EXTEND_EXP);
1238             AstNode* newp = nullptr;  // No change
1239             if (nodep->lhsp()->isSigned() && nodep->rhsp()->isSigned()) {
1240                 newp = new AstPowSS(nodep->fileline(), nodep->lhsp()->unlinkFrBack(),
1241                                     nodep->rhsp()->unlinkFrBack());
1242             } else if (nodep->lhsp()->isSigned() && !nodep->rhsp()->isSigned()) {
1243                 newp = new AstPowSU(nodep->fileline(), nodep->lhsp()->unlinkFrBack(),
1244                                     nodep->rhsp()->unlinkFrBack());
1245             } else if (!nodep->lhsp()->isSigned() && nodep->rhsp()->isSigned()) {
1246                 newp = new AstPowUS(nodep->fileline(), nodep->lhsp()->unlinkFrBack(),
1247                                     nodep->rhsp()->unlinkFrBack());
1248             }
1249             if (newp) {
1250                 newp->dtypeFrom(nodep);
1251                 UINFO(9, "powOld " << nodep << endl);
1252                 UINFO(9, "powNew " << newp << endl);
1253                 VL_DO_DANGLING(nodep->replaceWith(newp), nodep);
1254             }
1255         }
1256     }
visit(AstPowSU * nodep)1257     virtual void visit(AstPowSU* nodep) override {
1258         // POWSU/SS/US only created here, dtype already determined, so
1259         // nothing to do in this function
1260         userIterateAndNext(nodep->lhsp(), WidthVP(SELF, BOTH).p());
1261         userIterateAndNext(nodep->rhsp(), WidthVP(SELF, BOTH).p());
1262     }
visit(AstPowSS * nodep)1263     virtual void visit(AstPowSS* nodep) override {
1264         // POWSU/SS/US only created here, dtype already determined, so
1265         // nothing to do in this function
1266         userIterateAndNext(nodep->lhsp(), WidthVP(SELF, BOTH).p());
1267         userIterateAndNext(nodep->rhsp(), WidthVP(SELF, BOTH).p());
1268     }
visit(AstPowUS * nodep)1269     virtual void visit(AstPowUS* nodep) override {
1270         // POWSU/SS/US only created here, dtype already determined, so
1271         // nothing to do in this function
1272         userIterateAndNext(nodep->lhsp(), WidthVP(SELF, BOTH).p());
1273         userIterateAndNext(nodep->rhsp(), WidthVP(SELF, BOTH).p());
1274     }
visit(AstCountBits * nodep)1275     virtual void visit(AstCountBits* nodep) override {
1276         if (m_vup->prelim()) {
1277             iterateCheckSizedSelf(nodep, "LHS", nodep->lhsp(), SELF, BOTH);
1278             iterateCheckSizedSelf(nodep, "RHS", nodep->rhsp(), SELF, BOTH);
1279             iterateCheckSizedSelf(nodep, "THS", nodep->thsp(), SELF, BOTH);
1280             iterateCheckSizedSelf(nodep, "FHS", nodep->fhsp(), SELF, BOTH);
1281             // If it's a 32 bit number, we need a 6 bit number as we need to return '32'.
1282             const int selwidth = V3Number::log2b(nodep->lhsp()->width()) + 1;
1283             nodep->dtypeSetLogicSized(selwidth,
1284                                       VSigning::UNSIGNED);  // Spec doesn't indicate if an integer
1285         }
1286     }
visit(AstCountOnes * nodep)1287     virtual void visit(AstCountOnes* nodep) override {
1288         if (m_vup->prelim()) {
1289             iterateCheckSizedSelf(nodep, "LHS", nodep->lhsp(), SELF, BOTH);
1290             // If it's a 32 bit number, we need a 6 bit number as we need to return '32'.
1291             const int selwidth = V3Number::log2b(nodep->lhsp()->width()) + 1;
1292             nodep->dtypeSetLogicSized(selwidth,
1293                                       VSigning::UNSIGNED);  // Spec doesn't indicate if an integer
1294         }
1295     }
visit(AstCvtPackString * nodep)1296     virtual void visit(AstCvtPackString* nodep) override {
1297         // Opaque returns, so arbitrary
1298         userIterateAndNext(nodep->lhsp(), WidthVP(SELF, BOTH).p());
1299         // Type set in constructor
1300     }
visit(AstTimeImport * nodep)1301     virtual void visit(AstTimeImport* nodep) override {
1302         // LHS is a real number in seconds
1303         // Need to round to time units and precision
1304         userIterateAndNext(nodep->lhsp(), WidthVP(SELF, BOTH).p());
1305         const AstConst* const constp = VN_CAST(nodep->lhsp(), Const);
1306         if (!constp || !constp->isDouble()) nodep->v3fatalSrc("Times should be doubles");
1307         if (nodep->timeunit().isNone()) nodep->v3fatalSrc("$time import no units");
1308         double time = constp->num().toDouble();
1309         if (v3Global.rootp()->timeprecision().isNone()) nodep->v3fatalSrc("Never set precision?");
1310         time /= nodep->timeunit().multiplier();
1311         // IEEE claims you should round to time precision here, but no simulator seems to do this
1312         AstConst* const newp = new AstConst(nodep->fileline(), AstConst::RealDouble(), time);
1313         nodep->replaceWith(newp);
1314         VL_DO_DANGLING(nodep->deleteTree(), nodep);
1315     }
visit(AstTimingControl * nodep)1316     virtual void visit(AstTimingControl* nodep) override {
1317         nodep->v3warn(E_UNSUPPORTED, "Unsupported: timing control statement in this location\n"
1318                                          << nodep->warnMore()
1319                                          << "... Suggest have one timing control statement "
1320                                          << "per procedure, at the top of the procedure");
1321         VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
1322     }
visit(AstAttrOf * nodep)1323     virtual void visit(AstAttrOf* nodep) override {
1324         VL_RESTORER(m_attrp);
1325         m_attrp = nodep;
1326         userIterateAndNext(nodep->fromp(), WidthVP(SELF, BOTH).p());
1327         if (nodep->dimp()) userIterateAndNext(nodep->dimp(), WidthVP(SELF, BOTH).p());
1328         // Don't iterate children, don't want to lose VarRef.
1329         switch (nodep->attrType()) {
1330         case AstAttrType::VAR_BASE:
1331         case AstAttrType::MEMBER_BASE:
1332         case AstAttrType::ENUM_BASE:
1333             // Soon to be handled in V3LinkWidth SEL generation, under attrp() and newSubLsbOf
1334             break;
1335         case AstAttrType::DIM_DIMENSIONS:
1336         case AstAttrType::DIM_UNPK_DIMENSIONS: {
1337             UASSERT_OBJ(nodep->fromp() && nodep->fromp()->dtypep(), nodep, "Unsized expression");
1338             const std::pair<uint32_t, uint32_t> dim = nodep->fromp()->dtypep()->dimensions(true);
1339             const int val = (nodep->attrType() == AstAttrType::DIM_UNPK_DIMENSIONS
1340                                  ? dim.second
1341                                  : (dim.first + dim.second));
1342             nodep->replaceWith(new AstConst(nodep->fileline(), AstConst::Signed32(), val));
1343             VL_DO_DANGLING(nodep->deleteTree(), nodep);
1344             break;
1345         }
1346         case AstAttrType::DIM_BITS:
1347         case AstAttrType::DIM_HIGH:
1348         case AstAttrType::DIM_INCREMENT:
1349         case AstAttrType::DIM_LEFT:
1350         case AstAttrType::DIM_LOW:
1351         case AstAttrType::DIM_RIGHT:
1352         case AstAttrType::DIM_SIZE: {
1353             UASSERT_OBJ(nodep->fromp() && nodep->fromp()->dtypep(), nodep, "Unsized expression");
1354             AstNodeDType* const dtypep = nodep->fromp()->dtypep();
1355             if (VN_IS(dtypep, QueueDType)) {
1356                 switch (nodep->attrType()) {
1357                 case AstAttrType::DIM_SIZE: {
1358                     AstNode* const newp = new AstCMethodHard(
1359                         nodep->fileline(), nodep->fromp()->unlinkFrBack(), "size", nullptr);
1360                     newp->dtypeSetSigned32();
1361                     newp->didWidth(true);
1362                     newp->protect(false);
1363                     nodep->replaceWith(newp);
1364                     VL_DO_DANGLING(nodep->deleteTree(), nodep);
1365                     break;
1366                 }
1367                 case AstAttrType::DIM_LEFT:
1368                 case AstAttrType::DIM_LOW: {
1369                     AstNode* const newp = new AstConst(nodep->fileline(), AstConst::Signed32(), 0);
1370                     nodep->replaceWith(newp);
1371                     VL_DO_DANGLING(nodep->deleteTree(), nodep);
1372                     break;
1373                 }
1374                 case AstAttrType::DIM_RIGHT:
1375                 case AstAttrType::DIM_HIGH: {
1376                     AstNode* const sizep = new AstCMethodHard(
1377                         nodep->fileline(), nodep->fromp()->unlinkFrBack(), "size", nullptr);
1378                     sizep->dtypeSetSigned32();
1379                     sizep->didWidth(true);
1380                     sizep->protect(false);
1381                     AstNode* const newp
1382                         = new AstSub(nodep->fileline(), sizep,
1383                                      new AstConst(nodep->fileline(), AstConst::Signed32(), 1));
1384                     nodep->replaceWith(newp);
1385                     VL_DO_DANGLING(nodep->deleteTree(), nodep);
1386                     break;
1387                 }
1388                 case AstAttrType::DIM_INCREMENT: {
1389                     AstNode* const newp
1390                         = new AstConst(nodep->fileline(), AstConst::Signed32(), -1);
1391                     nodep->replaceWith(newp);
1392                     VL_DO_DANGLING(nodep->deleteTree(), nodep);
1393                     break;
1394                 }
1395                 case AstAttrType::DIM_BITS: {
1396                     nodep->v3warn(E_UNSUPPORTED, "Unsupported: $bits for queue");
1397                     break;
1398                 }
1399                 default: nodep->v3error("Unhandled attribute type");
1400                 }
1401             } else {
1402                 const std::pair<uint32_t, uint32_t> dimpair = dtypep->skipRefp()->dimensions(true);
1403                 const uint32_t msbdim = dimpair.first + dimpair.second;
1404                 if (!nodep->dimp() || msbdim < 1) {
1405                     if (VN_IS(dtypep, BasicDType) && dtypep->basicp()->isString()) {
1406                         // IEEE undocumented but $bits(string) must give length(string) * 8
1407                         AstNode* const newp = new AstShiftL{
1408                             nodep->fileline(),
1409                             new AstLenN{nodep->fileline(), nodep->fromp()->unlinkFrBack()},
1410                             new AstConst{nodep->fileline(), 3},  // * 8
1411                             32};
1412                         nodep->replaceWith(newp);
1413                         VL_DO_DANGLING(pushDeletep(nodep), nodep);
1414                     } else {
1415                         const int dim = 1;
1416                         AstConst* const newp
1417                             = dimensionValue(nodep->fileline(), dtypep, nodep->attrType(), dim);
1418                         nodep->replaceWith(newp);
1419                         VL_DO_DANGLING(nodep->deleteTree(), nodep);
1420                     }
1421                 } else if (VN_IS(nodep->dimp(), Const)) {
1422                     const int dim = VN_AS(nodep->dimp(), Const)->toSInt();
1423                     AstConst* const newp
1424                         = dimensionValue(nodep->fileline(), dtypep, nodep->attrType(), dim);
1425                     nodep->replaceWith(newp);
1426                     VL_DO_DANGLING(nodep->deleteTree(), nodep);
1427                 } else {  // Need a runtime lookup table.  Yuk.
1428                     UASSERT_OBJ(nodep->fromp() && dtypep, nodep, "Unsized expression");
1429                     AstVar* const varp = dimensionVarp(dtypep, nodep->attrType(), msbdim);
1430                     AstNode* const dimp = nodep->dimp()->unlinkFrBack();
1431                     AstVarRef* const varrefp
1432                         = new AstVarRef(nodep->fileline(), varp, VAccess::READ);
1433                     varrefp->classOrPackagep(v3Global.rootp()->dollarUnitPkgAddp());
1434                     AstNode* const newp = new AstArraySel(nodep->fileline(), varrefp, dimp);
1435                     nodep->replaceWith(newp);
1436                     VL_DO_DANGLING(nodep->deleteTree(), nodep);
1437                 }
1438             }
1439             break;
1440         }
1441         case AstAttrType::TYPENAME: {
1442             UASSERT_OBJ(nodep->fromp(), nodep, "Unprovided expression");
1443             const string result = nodep->fromp()->dtypep()->prettyDTypeName();
1444             AstNode* const newp = new AstConst(nodep->fileline(), AstConst::String(), result);
1445             nodep->replaceWith(newp);
1446             VL_DO_DANGLING(nodep->deleteTree(), nodep);
1447             break;
1448         }
1449         default: {
1450             // Everything else resolved earlier
1451             nodep->dtypeSetLogicUnsized(32, 1, VSigning::UNSIGNED);  // Approximation, unsized 32
1452             UINFO(1, "Missing ATTR type case node: " << nodep << endl);
1453             nodep->v3fatalSrc("Missing ATTR type case");
1454             break;
1455         }
1456         }
1457     }
visit(AstPull * nodep)1458     virtual void visit(AstPull* nodep) override {
1459         // May have select underneath, let seek natural size
1460         userIterateChildren(nodep, WidthVP(SELF, BOTH).p());
1461     }
visit(AstText * nodep)1462     virtual void visit(AstText* nodep) override {
1463         // Only used in CStmts which don't care....
1464     }
1465 
1466     // DTYPES
visit(AstNodeArrayDType * nodep)1467     virtual void visit(AstNodeArrayDType* nodep) override {
1468         if (nodep->didWidthAndSet()) return;  // This node is a dtype & not both PRELIMed+FINALed
1469 
1470         if (nodep->subDTypep() == nodep->basicp()) {  // Innermost dimension
1471             AstBasicDType* const basicp = nodep->basicp();
1472             // If basic dtype is LOGIC_IMPLICIT, it is actually 1 bit LOGIC
1473             if (basicp->implicit()) {
1474                 UASSERT_OBJ(basicp->width() <= 1, basicp,
1475                             "must be 1 bit but actually " << basicp->width() << " bits");
1476                 AstBasicDType* const newp = new AstBasicDType(
1477                     basicp->fileline(), AstBasicDTypeKwd::LOGIC, basicp->numeric());
1478                 newp->widthForce(1, 1);
1479                 basicp->replaceWith(newp);
1480                 VL_DO_DANGLING(pushDeletep(basicp), basicp);
1481             }
1482         }
1483         // Iterate into subDTypep() to resolve that type and update pointer.
1484         nodep->refDTypep(iterateEditMoveDTypep(nodep, nodep->subDTypep()));
1485         // Cleanup array size
1486         userIterateAndNext(nodep->rangep(), WidthVP(SELF, BOTH).p());
1487         nodep->dtypep(nodep);  // The array itself, not subDtype
1488         if (auto* const adtypep = VN_CAST(nodep, UnpackArrayDType)) {
1489             // Historically array elements have width of the ref type not the full array
1490             nodep->widthFromSub(nodep->subDTypep());
1491             if (nodep->subDTypep()->skipRefp()->isCompound()) adtypep->isCompound(true);
1492         } else {
1493             const int width = nodep->subDTypep()->width() * nodep->rangep()->elementsConst();
1494             nodep->widthForce(width, width);
1495         }
1496         UINFO(4, "dtWidthed " << nodep << endl);
1497     }
visit(AstAssocArrayDType * nodep)1498     virtual void visit(AstAssocArrayDType* nodep) override {
1499         if (nodep->didWidthAndSet()) return;  // This node is a dtype & not both PRELIMed+FINALed
1500         // Iterate into subDTypep() to resolve that type and update pointer.
1501         nodep->refDTypep(iterateEditMoveDTypep(nodep, nodep->subDTypep()));
1502         nodep->keyDTypep(iterateEditMoveDTypep(nodep, nodep->keyDTypep()));
1503         nodep->dtypep(nodep);  // The array itself, not subDtype
1504         UINFO(4, "dtWidthed " << nodep << endl);
1505     }
visit(AstBracketArrayDType * nodep)1506     virtual void visit(AstBracketArrayDType* nodep) override {
1507         // Type inserted only because parser didn't know elementsp() type
1508         // Resolve elementsp's type
1509         userIterateChildren(nodep, WidthVP(SELF, BOTH).p());
1510         // We must edit when dtype still under normal nodes and before type table
1511         // See notes in iterateEditMoveDTypep
1512         AstNodeDType* const childp = nodep->childDTypep();
1513         childp->unlinkFrBack();
1514         AstNode* const elementsp = nodep->elementsp()->unlinkFrBack();
1515         AstNode* newp;
1516         if (VN_IS(elementsp, Unbounded)) {
1517             newp = new AstQueueDType(nodep->fileline(), VFlagChildDType(), childp, nullptr);
1518             VL_DO_DANGLING(elementsp->deleteTree(), elementsp);
1519         } else if (AstNodeDType* const keyp = VN_CAST(elementsp, NodeDType)) {
1520             newp = new AstAssocArrayDType(nodep->fileline(), VFlagChildDType(), childp, keyp);
1521         } else {
1522             // Must be expression that is constant, but we'll determine that later
1523             newp = new AstUnpackArrayDType(
1524                 nodep->fileline(), VFlagChildDType(), childp,
1525                 new AstRange(nodep->fileline(), new AstConst(elementsp->fileline(), 0),
1526                              new AstSub(elementsp->fileline(), elementsp,
1527                                         new AstConst(elementsp->fileline(), 1))));
1528         }
1529         nodep->replaceWith(newp);
1530         VL_DO_DANGLING(nodep->deleteTree(), nodep);
1531         // Normally parent's iteration would cover this, but we might have entered by a specific
1532         // visit
1533         VL_DO_DANGLING(userIterate(newp, nullptr), newp);
1534     }
visit(AstDynArrayDType * nodep)1535     virtual void visit(AstDynArrayDType* nodep) override {
1536         if (nodep->didWidthAndSet()) return;  // This node is a dtype & not both PRELIMed+FINALed
1537         // Iterate into subDTypep() to resolve that type and update pointer.
1538         nodep->refDTypep(iterateEditMoveDTypep(nodep, nodep->subDTypep()));
1539         nodep->dtypep(nodep);  // The array itself, not subDtype
1540         UINFO(4, "dtWidthed " << nodep << endl);
1541     }
visit(AstQueueDType * nodep)1542     virtual void visit(AstQueueDType* nodep) override {
1543         if (nodep->didWidthAndSet()) return;  // This node is a dtype & not both PRELIMed+FINALed
1544         // Iterate into subDTypep() to resolve that type and update pointer.
1545         nodep->refDTypep(iterateEditMoveDTypep(nodep, nodep->subDTypep()));
1546         nodep->dtypep(nodep);  // The array itself, not subDtype
1547         if (VN_IS(nodep->boundp(), Unbounded)) {
1548             nodep->boundp()->unlinkFrBack()->deleteTree();  // nullptr will represent unbounded
1549         }
1550         UINFO(4, "dtWidthed " << nodep << endl);
1551     }
visit(AstVoidDType * nodep)1552     virtual void visit(AstVoidDType* nodep) override {
1553         if (nodep->didWidthAndSet()) return;  // This node is a dtype & not both PRELIMed+FINALed
1554         nodep->dtypep(nodep);
1555         UINFO(4, "dtWidthed " << nodep << endl);
1556     }
visit(AstUnsizedArrayDType * nodep)1557     virtual void visit(AstUnsizedArrayDType* nodep) override {
1558         if (nodep->didWidthAndSet()) return;  // This node is a dtype & not both PRELIMed+FINALed
1559         // Iterate into subDTypep() to resolve that type and update pointer.
1560         nodep->refDTypep(iterateEditMoveDTypep(nodep, nodep->subDTypep()));
1561         // Cleanup array size
1562         nodep->dtypep(nodep);  // The array itself, not subDtype
1563         UINFO(4, "dtWidthed " << nodep << endl);
1564     }
visit(AstBasicDType * nodep)1565     virtual void visit(AstBasicDType* nodep) override {
1566         if (nodep->didWidthAndSet()) return;  // This node is a dtype & not both PRELIMed+FINALed
1567         if (nodep->generic()) return;  // Already perfect
1568         if (nodep->rangep()) {
1569             userIterateAndNext(nodep->rangep(), WidthVP(SELF, BOTH).p());
1570             // Because this DType has a unique child range, we know it's not
1571             // pointed at by other nodes unless they are referencing this type.
1572             // Furthermore the width() calculation would return identical
1573             // values.  Therefore we can directly replace the width
1574             nodep->widthForce(nodep->rangep()->elementsConst(), nodep->rangep()->elementsConst());
1575         } else if (nodep->isRanged()) {
1576             nodep->widthForce(nodep->nrange().elements(), nodep->nrange().elements());
1577         } else if (nodep->implicit()) {
1578             // Parameters may notice implicitness and change to different dtype
1579             nodep->widthForce(1, 1);
1580         }
1581         // else width in node is correct; it was set based on keyword().width()
1582         // at construction time.  Ditto signed, so "unsigned byte" etc works right.
1583         nodep->cvtRangeConst();
1584         // TODO: If BasicDType now looks like a generic type, we can convert to a real generic
1585         // dtype Instead for now doing this in V3WidthCommit
1586         UINFO(4, "dtWidthed " << nodep << endl);
1587     }
visit(AstConstDType * nodep)1588     virtual void visit(AstConstDType* nodep) override {
1589         if (nodep->didWidthAndSet()) return;  // This node is a dtype & not both PRELIMed+FINALed
1590         // Iterate into subDTypep() to resolve that type and update pointer.
1591         nodep->refDTypep(iterateEditMoveDTypep(nodep, nodep->subDTypep()));
1592         userIterateChildren(nodep, nullptr);
1593         nodep->dtypep(nodep);  // Should already be set, but be clear it's not the subDType
1594         nodep->widthFromSub(nodep->subDTypep());
1595         UINFO(4, "dtWidthed " << nodep << endl);
1596     }
visit(AstRefDType * nodep)1597     virtual void visit(AstRefDType* nodep) override {
1598         if (nodep->doingWidth()) {  // Early exit if have circular parameter definition
1599             nodep->v3error("Typedef's type is circular: " << nodep->prettyName());
1600             nodep->dtypeSetBit();
1601             nodep->doingWidth(false);
1602             return;
1603         }
1604         if (nodep->didWidthAndSet()) return;  // This node is a dtype & not both PRELIMed+FINALed
1605         nodep->doingWidth(true);
1606         if (nodep->typeofp()) {  // type(typeofp_expression)
1607             // Type comes from expression's type
1608             userIterateAndNext(nodep->typeofp(), WidthVP(SELF, BOTH).p());
1609             AstNode* const typeofp = nodep->typeofp();
1610             nodep->typedefp(nullptr);
1611             nodep->refDTypep(typeofp->dtypep());
1612             VL_DO_DANGLING(typeofp->unlinkFrBack()->deleteTree(), typeofp);
1613             // We had to use AstRefDType for this construct as pointers to this type
1614             // in type table are still correct (which they wouldn't be if we replaced the node)
1615         }
1616         userIterateChildren(nodep, nullptr);
1617         if (nodep->subDTypep()) {
1618             // Normally iterateEditMoveDTypep iterate would work, but the refs are under
1619             // the TypeDef which will upset iterateEditMoveDTypep as it can't find it under
1620             // this node's childDTypep
1621             userIterate(nodep->subDTypep(), nullptr);
1622             nodep->refDTypep(iterateEditMoveDTypep(nodep, nodep->subDTypep()));
1623             nodep->typedefp(nullptr);  // Note until line above subDTypep() may have followed this
1624             // Widths are resolved, but special iterate to check for recurstion
1625             userIterate(nodep->subDTypep(), nullptr);
1626         }
1627         // Effectively nodep->dtypeFrom(nodep->dtypeSkipRefp());
1628         // But might be recursive, so instead manually recurse into the referenced type
1629         UASSERT_OBJ(nodep->subDTypep(), nodep, "Unlinked");
1630         nodep->dtypeFrom(nodep->subDTypep());
1631         nodep->widthFromSub(nodep->subDTypep());
1632         UINFO(4, "dtWidthed " << nodep << endl);
1633         nodep->doingWidth(false);
1634     }
visit(AstTypedef * nodep)1635     virtual void visit(AstTypedef* nodep) override {
1636         if (nodep->didWidthAndSet()) return;  // This node is a dtype & not both PRELIMed+FINALed
1637         if (auto* const refp = checkRefToTypedefRecurse(nodep, nodep)) {
1638             nodep->v3error("Typedef has self-reference: " << nodep->prettyNameQ() << '\n'
1639                                                           << nodep->warnContextPrimary() << '\n'
1640                                                           << refp->warnOther()
1641                                                           << "... Location of reference\n"
1642                                                           << refp->warnContextSecondary());
1643             // May cause internel error but avoids infinite loop on dump
1644             refp->typedefp(nullptr);
1645             VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
1646             return;
1647         }
1648         nodep->dtypep(iterateEditMoveDTypep(nodep, nodep->subDTypep()));
1649         userIterateChildren(nodep, nullptr);
1650     }
visit(AstParamTypeDType * nodep)1651     virtual void visit(AstParamTypeDType* nodep) override {
1652         if (nodep->didWidthAndSet()) return;  // This node is a dtype & not both PRELIMed+FINALed
1653         nodep->dtypep(iterateEditMoveDTypep(nodep, nodep->subDTypep()));
1654         userIterateChildren(nodep, nullptr);
1655         nodep->widthFromSub(nodep->subDTypep());
1656     }
visit(AstCastDynamic * nodep)1657     virtual void visit(AstCastDynamic* nodep) override {
1658         nodep->dtypeChgWidthSigned(32, 1, VSigning::SIGNED);  // Spec says integer return
1659         userIterateChildren(nodep, WidthVP(SELF, BOTH).p());
1660         AstNodeDType* const toDtp = nodep->top()->dtypep()->skipRefToEnump();
1661         AstNodeDType* const fromDtp = nodep->fromp()->dtypep()->skipRefToEnump();
1662         FileLine* const fl = nodep->fileline();
1663         const auto castable = computeCastable(toDtp, fromDtp, nodep->fromp());
1664         AstNode* newp;
1665         if (castable == DYNAMIC_CLASS) {
1666             // Keep in place, will compute at runtime
1667             return;
1668         } else if (castable == DYNAMIC_ENUM) {
1669             // TODO is from is a constant we could simplify, though normal constant
1670             // elimination should do much the same
1671             // Form: "( ((v > size) ? false : enum_valid[v[N:0]])
1672             //          ? ExprStmt(ExprAssign(out, Cast(v, type)), 1) : 0)"
1673             auto* const enumDtp = VN_AS(toDtp, EnumDType);
1674             UASSERT_OBJ(enumDtp, nodep, "$cast determined as enum, but not enum type");
1675             const uint64_t maxval = enumMaxValue(nodep, enumDtp);
1676             const int selwidth = V3Number::log2b(maxval) + 1;  // Width to address a bit
1677             AstVar* const varp
1678                 = enumVarp(enumDtp, AstAttrType::ENUM_VALID, (1ULL << selwidth) - 1);
1679             AstVarRef* const varrefp = new AstVarRef(fl, varp, VAccess::READ);
1680             varrefp->classOrPackagep(v3Global.rootp()->dollarUnitPkgAddp());
1681             FileLine* const fl_nowarn = new FileLine(fl);
1682             fl_nowarn->warnOff(V3ErrorCode::WIDTH, true);
1683             auto* const testp = new AstCond{
1684                 fl,
1685                 new AstGt{fl_nowarn, nodep->fromp()->cloneTree(false),
1686                           new AstConst{fl_nowarn, AstConst::Unsized64{}, maxval}},
1687                 new AstConst{fl, AstConst::BitFalse{}},
1688                 new AstArraySel{fl, varrefp,
1689                                 new AstSel{fl, nodep->fromp()->cloneTree(false), 0, selwidth}}};
1690             newp = new AstCond{fl, testp,
1691                                new AstExprStmt{fl,
1692                                                new AstAssign{fl, nodep->top()->unlinkFrBack(),
1693                                                              nodep->fromp()->unlinkFrBack()},
1694                                                new AstConst{fl, AstConst::Signed32(), 1}},
1695                                new AstConst{fl, AstConst::Signed32(), 0}};
1696         } else if (castable == COMPATIBLE) {
1697             nodep->v3warn(CASTCONST, "$cast will always return one as "
1698                                          << toDtp->prettyDTypeNameQ()
1699                                          << " is always castable from "
1700                                          << fromDtp->prettyDTypeNameQ() << '\n'
1701                                          << nodep->warnMore() << "... Suggest static cast");
1702             newp = new AstExprStmt{
1703                 fl,
1704                 new AstAssign{fl, nodep->top()->unlinkFrBack(),
1705                               new AstCast{fl, nodep->fromp()->unlinkFrBack(), toDtp}},
1706                 new AstConst{fl, AstConst::Signed32(), 1}};
1707         } else if (castable == INCOMPATIBLE) {
1708             newp = new AstConst{fl, 0};
1709             nodep->v3warn(CASTCONST, "$cast will always return zero as "
1710                                          << toDtp->prettyDTypeNameQ() << " is not castable from "
1711                                          << fromDtp->prettyDTypeNameQ());
1712         } else {
1713             newp = new AstConst{fl, 0};
1714             nodep->v3warn(E_UNSUPPORTED, "Unsupported: $cast to "
1715                                              << toDtp->prettyDTypeNameQ() << " from "
1716                                              << fromDtp->prettyDTypeNameQ() << '\n'
1717                                              << nodep->warnMore()
1718                                              << "... Suggest try static cast");
1719         }
1720         newp->dtypeFrom(nodep);
1721         nodep->replaceWith(newp);
1722         VL_DO_DANGLING(pushDeletep(nodep), nodep);
1723         userIterate(newp, m_vup);
1724     }
visit(AstCastParse * nodep)1725     virtual void visit(AstCastParse* nodep) override {
1726         // nodep->dtp could be data type, or a primary_constant
1727         // Don't iterate lhsp, will deal with that once convert the type
1728         V3Const::constifyParamsEdit(nodep->dtp());  // itemp may change
1729         if (AstConst* const constp = VN_CAST(nodep->dtp(), Const)) {
1730             constp->unlinkFrBack();
1731             AstNode* const newp
1732                 = new AstCastSize(nodep->fileline(), nodep->lhsp()->unlinkFrBack(), constp);
1733             nodep->replaceWith(newp);
1734             VL_DO_DANGLING(pushDeletep(nodep), nodep);
1735             userIterate(newp, m_vup);
1736         } else {
1737             nodep->v3warn(E_UNSUPPORTED,
1738                           "Unsupported: Cast to " << nodep->dtp()->prettyTypeName());
1739             nodep->replaceWith(nodep->lhsp()->unlinkFrBack());
1740         }
1741     }
visit(AstCast * nodep)1742     virtual void visit(AstCast* nodep) override {
1743         nodep->dtypep(iterateEditMoveDTypep(nodep, nodep->subDTypep()));
1744         if (m_vup->prelim()) {
1745             // if (debug()) nodep->dumpTree(cout, "  CastPre: ");
1746             userIterateAndNext(nodep->fromp(), WidthVP(SELF, PRELIM).p());
1747             AstNodeDType* const toDtp = nodep->dtypep()->skipRefToEnump();
1748             AstNodeDType* const fromDtp = nodep->fromp()->dtypep()->skipRefToEnump();
1749             const auto castable = computeCastable(toDtp, fromDtp, nodep->fromp());
1750             bool bad = false;
1751             if (castable == UNSUPPORTED) {
1752                 nodep->v3warn(E_UNSUPPORTED, "Unsupported: static cast to "
1753                                                  << toDtp->prettyDTypeNameQ() << " from "
1754                                                  << fromDtp->prettyDTypeNameQ());
1755                 bad = true;
1756             } else if (castable == COMPATIBLE || castable == DYNAMIC_ENUM) {
1757                 ;  // Continue
1758             } else if (castable == DYNAMIC_CLASS) {
1759                 nodep->v3error("Dynamic, not static cast, required to cast "
1760                                << toDtp->prettyDTypeNameQ() << " from "
1761                                << fromDtp->prettyDTypeNameQ() << '\n'
1762                                << nodep->warnMore() << "... Suggest dynamic $cast");
1763                 bad = true;
1764             } else if (castable == INCOMPATIBLE) {
1765                 nodep->v3error("Incompatible types to static cast to "
1766                                << toDtp->prettyDTypeNameQ() << " from "
1767                                << fromDtp->prettyDTypeNameQ() << '\n');
1768                 bad = true;
1769             } else {
1770                 nodep->v3fatalSrc("bad casting case");
1771             }
1772             // For now, replace it ASAP, so widthing can propagate easily
1773             // The cast may change signing, but we don't know the sign yet.  Make it so.
1774             // Note we don't sign fromp() that would make the algorithm O(n^2) if lots of casting.
1775             AstNode* newp = nullptr;
1776             if (bad) {
1777             } else if (const AstBasicDType* const basicp = toDtp->basicp()) {
1778                 if (!basicp->isDouble() && !fromDtp->isDouble()) {
1779                     const int width = toDtp->width();
1780                     castSized(nodep, nodep->fromp(), width);
1781                     // Note castSized might modify nodep->fromp()
1782                 } else {
1783                     iterateCheck(nodep, "value", nodep->lhsp(), SELF, FINAL, fromDtp, EXTEND_EXP,
1784                                  false);
1785                 }
1786                 if (basicp->isDouble() && !nodep->fromp()->isDouble()) {
1787                     if (nodep->fromp()->isSigned()) {
1788                         newp = new AstISToRD(nodep->fileline(), nodep->fromp()->unlinkFrBack());
1789                     } else {
1790                         newp = new AstIToRD(nodep->fileline(), nodep->fromp()->unlinkFrBack());
1791                     }
1792                 } else if (!basicp->isDouble() && nodep->fromp()->isDouble()) {
1793                     if (basicp->isSigned()) {
1794                         newp
1795                             = new AstRToIRoundS(nodep->fileline(), nodep->fromp()->unlinkFrBack());
1796                     } else {
1797                         newp = new AstUnsigned(
1798                             nodep->fileline(),
1799                             new AstRToIS(nodep->fileline(), nodep->fromp()->unlinkFrBack()));
1800                     }
1801                 } else if (basicp->isSigned() && !nodep->fromp()->isSigned()) {
1802                     newp = new AstSigned(nodep->fileline(), nodep->fromp()->unlinkFrBack());
1803                 } else if (!basicp->isSigned() && nodep->fromp()->isSigned()) {
1804                     newp = new AstUnsigned(nodep->fileline(), nodep->fromp()->unlinkFrBack());
1805                 } else {
1806                     // Can just remove cast
1807                 }
1808             } else if (VN_IS(toDtp, ClassRefDType)) {
1809                 // Can just remove cast
1810             } else {
1811                 nodep->v3fatalSrc("Unimplemented: Casting non-simple data type "
1812                                   << toDtp->prettyDTypeNameQ());
1813             }
1814             if (!newp) newp = nodep->fromp()->unlinkFrBack();
1815             nodep->lhsp(newp);
1816             // if (debug()) nodep->dumpTree(cout, "  CastOut: ");
1817             // if (debug()) nodep->backp()->dumpTree(cout, "  CastOutUpUp: ");
1818         }
1819         if (m_vup->final()) {
1820             iterateCheck(nodep, "value", nodep->lhsp(), SELF, FINAL, nodep->lhsp()->dtypep(),
1821                          EXTEND_EXP, false);
1822             AstNode* const underp = nodep->lhsp()->unlinkFrBack();
1823             // if (debug()) underp->dumpTree(cout, "  CastRep: ");
1824             nodep->replaceWith(underp);
1825             VL_DO_DANGLING(pushDeletep(nodep), nodep);
1826         }
1827     }
visit(AstCastSize * nodep)1828     virtual void visit(AstCastSize* nodep) override {
1829         // IEEE: Signedness of result is same as self-determined signedness
1830         // However, the result is same as BITSEL, so we do not sign extend the LHS
1831         UASSERT_OBJ(VN_IS(nodep->rhsp(), Const), nodep, "Unsupported: Non-const cast of size");
1832         // if (debug()) nodep->dumpTree(cout, "  CastSizePre: ");
1833         if (m_vup->prelim()) {
1834             int width = VN_AS(nodep->rhsp(), Const)->toSInt();
1835             if (width < 1) {
1836                 nodep->v3error("Size-changing cast to zero or negative size");
1837                 width = 1;
1838             }
1839             userIterateAndNext(nodep->lhsp(), WidthVP(SELF, PRELIM).p());
1840             castSized(nodep, nodep->lhsp(), width);  // lhsp may change
1841         }
1842         if (m_vup->final()) {
1843             // CastSize not needed once sizes determined
1844             AstNode* const underp = nodep->lhsp()->unlinkFrBack();
1845             underp->dtypeFrom(nodep);
1846             nodep->replaceWith(underp);
1847             VL_DO_DANGLING(pushDeletep(nodep), nodep);
1848         }
1849         // if (debug()) nodep->dumpTree(cout, "  CastSizeOut: ");
1850     }
castSized(AstNode * nodep,AstNode * underp,int width)1851     void castSized(AstNode* nodep, AstNode* underp, int width) {
1852         const AstBasicDType* underDtp = VN_CAST(underp->dtypep(), BasicDType);
1853         if (!underDtp) underDtp = underp->dtypep()->basicp();
1854         if (!underDtp) {
1855             nodep->v3warn(E_UNSUPPORTED, "Unsupported: Size-changing cast on non-basic data type");
1856             underDtp = VN_AS(nodep->findBitDType(), BasicDType);
1857         }
1858         UASSERT_OBJ(underp == nodep->op1p(), nodep, "Assuming op1 is cast value");
1859         // A cast propagates its size to the lower expression and is included in the maximum
1860         // width, so 23'(1'b1 + 1'b1) uses 23-bit math, but 1'(2'h2 * 2'h1) uses two-bit math.
1861         // However the output width is exactly that requested.
1862         // So two steps, first do the calculation's width (max of the two widths)
1863         {
1864             const int calcWidth = std::max(width, underDtp->width());
1865             AstNodeDType* const calcDtp
1866                 = (underDtp->isFourstate()
1867                        ? nodep->findLogicDType(calcWidth, calcWidth, underDtp->numeric())
1868                        : nodep->findBitDType(calcWidth, calcWidth, underDtp->numeric()));
1869             nodep->dtypep(calcDtp);
1870             // We ignore warnings as that is sort of the point of a cast
1871             iterateCheck(nodep, "Cast expr", underp, CONTEXT, FINAL, calcDtp, EXTEND_EXP, false);
1872             VL_DANGLING(underp);
1873             underp = nodep->op1p();  // Above asserts that op1 was underp pre-relink
1874         }
1875         // if (debug()) nodep->dumpTree(cout, "  CastSizeClc: ");
1876         // Next step, make the proper output width
1877         {
1878             AstNodeDType* const outDtp
1879                 = (underDtp->isFourstate()
1880                        ? nodep->findLogicDType(width, width, underDtp->numeric())
1881                        : nodep->findBitDType(width, width, underDtp->numeric()));
1882             nodep->dtypep(outDtp);
1883             // We ignore warnings as that is sort of the point of a cast
1884             widthCheckSized(nodep, "Cast expr", underp, outDtp, EXTEND_EXP, false);
1885             VL_DANGLING(underp);
1886         }
1887     }
visit(AstVar * nodep)1888     virtual void visit(AstVar* nodep) override {
1889         // if (debug()) nodep->dumpTree(cout, "  InitPre: ");
1890         // Must have deterministic constant width
1891         // We can't skip this step when width()!=0, as creating a AstVar
1892         // with non-constant range gets size 1, not size 0.  So use didWidth().
1893         if (nodep->didWidth()) return;
1894         if (nodep->doingWidth()) {  // Early exit if have circular parameter definition
1895             UASSERT_OBJ(nodep->valuep(), nodep, "circular, but without value");
1896             nodep->v3error("Variable's initial value is circular: " << nodep->prettyNameQ());
1897             pushDeletep(nodep->valuep()->unlinkFrBack());
1898             nodep->valuep(new AstConst(nodep->fileline(), AstConst::BitTrue()));
1899             nodep->dtypeFrom(nodep->valuep());
1900             nodep->didWidth(true);
1901             return;
1902         }
1903         nodep->doingWidth(true);
1904         // Make sure dtype is sized
1905         nodep->dtypep(iterateEditMoveDTypep(nodep, nodep->subDTypep()));
1906         UASSERT_OBJ(nodep->dtypep(), nodep, "No dtype determined for var");
1907         if (const AstUnsizedArrayDType* const unsizedp
1908             = VN_CAST(nodep->dtypeSkipRefp(), UnsizedArrayDType)) {
1909             if (!(m_ftaskp && m_ftaskp->dpiImport())) {
1910                 UINFO(9, "Unsized becomes dynamic array " << nodep << endl);
1911                 AstDynArrayDType* const newp
1912                     = new AstDynArrayDType(unsizedp->fileline(), unsizedp->subDTypep());
1913                 nodep->dtypep(newp);
1914                 v3Global.rootp()->typeTablep()->addTypesp(newp);
1915             }
1916         }
1917         if (VN_IS(nodep->dtypep()->skipRefToConstp(), ConstDType)) nodep->isConst(true);
1918         // Parameters if implicit untyped inherit from what they are assigned to
1919         const AstBasicDType* const bdtypep = VN_CAST(nodep->dtypep(), BasicDType);
1920         bool didchk = false;
1921         const bool implicitParam = nodep->isParam() && bdtypep && bdtypep->implicit();
1922         if (implicitParam) {
1923             if (nodep->valuep()) {
1924                 userIterateAndNext(nodep->valuep(), WidthVP(nodep->dtypep(), PRELIM).p());
1925                 UINFO(9, "implicitParamPRELIMIV " << nodep->valuep() << endl);
1926                 // Although nodep will get a different width for parameters
1927                 // just below, we want the init numbers to retain their
1928                 // width/minwidth until parameters are replaced.
1929                 // This prevents width warnings at the location the parameter is substituted in
1930                 if (nodep->valuep()->isDouble()) {
1931                     nodep->dtypeSetDouble();
1932                     VL_DANGLING(bdtypep);
1933                 } else {
1934                     int width = 0;
1935                     const AstBasicDType* const valueBdtypep = nodep->valuep()->dtypep()->basicp();
1936                     bool issigned = false;
1937                     if (bdtypep->isNosign()) {
1938                         if (valueBdtypep && valueBdtypep->isSigned()) issigned = true;
1939                     } else {
1940                         issigned = bdtypep->isSigned();
1941                     }
1942                     if (valueBdtypep->isString()) {
1943                         // parameter X = "str", per IEEE is a number, not a string
1944                         if (const auto* const constp = VN_CAST(nodep->valuep(), Const)) {
1945                             if (constp->num().isString()) {
1946                                 width = constp->num().toString().length() * 8;
1947                             }
1948                         }
1949                         if (width < 8) width = 8;
1950                     } else if (nodep->valuep()->dtypep()->widthSized()) {
1951                         width = nodep->valuep()->width();
1952                     } else {
1953                         if (nodep->valuep()->width() > 32) {
1954                             nodep->valuep()->v3warn(
1955                                 WIDTH,
1956                                 "Assigning >32 bit to unranged parameter (defaults to 32 bits)");
1957                         }
1958                         width = 32;
1959                     }
1960                     // Can't just inherit valuep()->dtypep() as mwidth might not equal width
1961                     if (width == 1) {
1962                         // one bit parameter is same as "parameter [0] foo",
1963                         // not "parameter logic foo" as you can extract
1964                         // "foo[0]" from a parameter but not a wire
1965                         nodep->dtypeChgWidthSigned(width, nodep->valuep()->widthMin(),
1966                                                    VSigning::fromBool(issigned));
1967                         nodep->dtypep(nodep->findLogicRangeDType(VNumRange{0, 0},
1968                                                                  nodep->valuep()->widthMin(),
1969                                                                  VSigning::fromBool(issigned)));
1970                     } else {
1971                         nodep->dtypeChgWidthSigned(width, nodep->valuep()->widthMin(),
1972                                                    VSigning::fromBool(issigned));
1973                     }
1974                     didchk = true;
1975                 }
1976                 iterateCheckAssign(nodep, "Initial value", nodep->valuep(), FINAL,
1977                                    nodep->dtypep());
1978                 UINFO(9, "implicitParamFromIV " << nodep->valuep() << endl);
1979                 // UINFO below will print variable nodep
1980             } else {
1981                 // Or, if nothing assigned, they're integral
1982                 nodep->dtypeSetSigned32();
1983                 VL_DANGLING(bdtypep);
1984             }
1985         } else if (bdtypep && bdtypep->implicit()) {  // Implicits get converted to size 1
1986             nodep->dtypeSetLogicSized(1, bdtypep->numeric());
1987             VL_DANGLING(bdtypep);
1988         }
1989         if (nodep->valuep() && !didchk) {
1990             // if (debug()) nodep->dumpTree(cout, "  final: ");
1991             // AstPattern requires assignments to pass datatype on PRELIM
1992             userIterateAndNext(nodep->valuep(), WidthVP(nodep->dtypep(), PRELIM).p());
1993             iterateCheckAssign(nodep, "Initial value", nodep->valuep(), FINAL, nodep->dtypep());
1994         }
1995         UINFO(4, "varWidthed " << nodep << endl);
1996         // if (debug()) nodep->dumpTree(cout, "  InitOut: ");
1997         nodep->didWidth(true);
1998         nodep->doingWidth(false);
1999     }
visit(AstNodeVarRef * nodep)2000     virtual void visit(AstNodeVarRef* nodep) override {
2001         if (nodep->didWidth()) return;
2002         if (!nodep->varp()) {
2003             if (m_paramsOnly && VN_IS(nodep, VarXRef)) {
2004                 checkConstantOrReplace(
2005                     nodep, "Parameter-resolved constants must not use dotted references: "
2006                                + nodep->prettyNameQ());
2007                 VL_DANGLING(nodep);
2008                 return;
2009             } else {
2010                 nodep->v3fatalSrc("Unlinked varref");
2011             }
2012         }
2013         if (!nodep->varp()->didWidth()) {
2014             // Var hasn't been widthed, so make it so.
2015             userIterate(nodep->varp(), nullptr);
2016         }
2017         // if (debug()>=9) { nodep->dumpTree(cout, "  VRin  ");
2018         //  nodep->varp()->dumpTree(cout, " forvar "); }
2019         // Note genvar's are also entered as integers
2020         nodep->dtypeFrom(nodep->varp());
2021         if (VN_IS(nodep->backp(), NodeAssign) && nodep->access().isWriteOrRW()) {  // On LHS
2022             UASSERT_OBJ(nodep->dtypep(), nodep, "LHS var should be dtype completed");
2023         }
2024         // if (debug() >= 9) nodep->dumpTree(cout, "  VRout ");
2025         if (nodep->access().isWriteOrRW() && nodep->varp()->direction() == VDirection::CONSTREF) {
2026             nodep->v3error("Assigning to const ref variable: " << nodep->prettyNameQ());
2027         } else if (nodep->access().isWriteOrRW() && nodep->varp()->isConst() && !m_paramsOnly
2028                    && (!m_ftaskp || !m_ftaskp->isConstructor()) && !VN_IS(m_procedurep, Initial)) {
2029             // Too loose, but need to allow our generated first assignment
2030             // Move this to a property of the AstInitial block
2031             nodep->v3error("Assigning to const variable: " << nodep->prettyNameQ());
2032         }
2033         nodep->didWidth(true);
2034     }
2035 
visit(AstEnumDType * nodep)2036     virtual void visit(AstEnumDType* nodep) override {
2037         if (nodep->didWidthAndSet()) return;  // This node is a dtype & not both PRELIMed+FINALed
2038         UINFO(5, "  ENUMDTYPE " << nodep << endl);
2039         nodep->refDTypep(iterateEditMoveDTypep(nodep, nodep->subDTypep()));
2040         nodep->dtypep(nodep);
2041         nodep->widthFromSub(nodep->subDTypep());
2042         // Assign widths
2043         userIterateAndNext(nodep->itemsp(), WidthVP(nodep->dtypep(), BOTH).p());
2044         // Assign missing values
2045         V3Number num(nodep, nodep->width(), 0);
2046         const V3Number one{nodep, nodep->width(), 1};
2047         std::map<const V3Number, AstEnumItem*> inits;
2048         for (AstEnumItem* itemp = nodep->itemsp(); itemp;
2049              itemp = VN_AS(itemp->nextp(), EnumItem)) {
2050             if (itemp->valuep()) {
2051                 if (debug() >= 9) {
2052                     UINFO(0, "EnumInit " << itemp << endl);
2053                     itemp->valuep()->dumpTree(cout, "-EnumInit: ");
2054                 }
2055                 V3Const::constifyParamsEdit(itemp->valuep());  // itemp may change
2056                 if (!VN_IS(itemp->valuep(), Const)) {
2057                     itemp->valuep()->v3error("Enum value isn't a constant");
2058                     itemp->valuep()->unlinkFrBack()->deleteTree();
2059                     continue;
2060                 }
2061                 // TODO IEEE says assigning sized number that is not same size as enum is illegal
2062             }
2063             if (!itemp->valuep()) {
2064                 if (num.isEqZero() && itemp != nodep->itemsp()) {
2065                     itemp->v3error("Enum value illegally wrapped around (IEEE 1800-2017 6.19)");
2066                 }
2067                 if (num.isFourState()) {
2068                     itemp->v3error("Enum value that is unassigned cannot follow value with X/Zs "
2069                                    "(IEEE 1800-2017 6.19)");
2070                 }
2071                 if (!nodep->dtypep()->basicp()
2072                     && !nodep->dtypep()->basicp()->keyword().isIntNumeric()) {
2073                     itemp->v3error("Enum names without values only allowed on numeric types");
2074                     // as can't +1 to resolve them.
2075                 }
2076                 itemp->valuep(new AstConst(itemp->fileline(), num));
2077             }
2078 
2079             const AstConst* const constp = VN_AS(itemp->valuep(), Const);
2080             if (constp->num().isFourState() && nodep->dtypep()->basicp()
2081                 && !nodep->dtypep()->basicp()->isFourstate()) {
2082                 itemp->v3error("Enum value with X/Zs cannot be assigned to non-fourstate type "
2083                                "(IEEE 1800-2017 6.19)");
2084             }
2085             num.opAssign(constp->num());
2086             // Look for duplicates
2087             if (inits.find(num) != inits.end()) {  // IEEE says illegal
2088                 const AstNode* const otherp = inits.find(num)->second;
2089                 itemp->v3error("Overlapping enumeration value: "
2090                                << itemp->prettyNameQ() << '\n'
2091                                << itemp->warnContextPrimary() << '\n'
2092                                << otherp->warnOther() << "... Location of original declaration\n"
2093                                << otherp->warnContextSecondary());
2094             } else {
2095                 inits.emplace(num, itemp);
2096             }
2097             num.opAdd(one, constp->num());
2098         }
2099     }
visit(AstEnumItem * nodep)2100     virtual void visit(AstEnumItem* nodep) override {
2101         UINFO(5, "   ENUMITEM " << nodep << endl);
2102         AstNodeDType* const vdtypep = m_vup->dtypep();
2103         UASSERT_OBJ(vdtypep, nodep, "ENUMITEM not under ENUM");
2104         nodep->dtypep(vdtypep);
2105         if (nodep->valuep()) {  // else the value will be assigned sequentially
2106             // Default type is int, but common to assign narrower values, so minwidth from value
2107             userIterateAndNext(nodep->valuep(), WidthVP(CONTEXT, PRELIM).p());
2108             // Minwidth does not come from value, as spec says set based on parent
2109             // and if we keep minwidth we'll consider it unsized which is incorrect
2110             iterateCheck(nodep, "Enum value", nodep->valuep(), CONTEXT, FINAL, nodep->dtypep(),
2111                          EXTEND_EXP);
2112         }
2113     }
visit(AstEnumItemRef * nodep)2114     virtual void visit(AstEnumItemRef* nodep) override {
2115         if (!nodep->itemp()->didWidth()) {
2116             // We need to do the whole enum en-mass
2117             AstNode* enump = nodep->itemp();
2118             UASSERT_OBJ(enump, nodep, "EnumItemRef not linked");
2119             for (; enump; enump = enump->backp()) {
2120                 if (VN_IS(enump, EnumDType)) break;
2121             }
2122             UASSERT_OBJ(enump, nodep, "EnumItemRef can't deref back to an Enum");
2123             VL_DO_DANGLING(userIterate(enump, m_vup), enump);  // Parent enump maybe relinked
2124         }
2125         nodep->dtypeFrom(nodep->itemp());
2126     }
visit(AstConsAssoc * nodep)2127     virtual void visit(AstConsAssoc* nodep) override {
2128         // Type computed when constructed here
2129         auto* const vdtypep = VN_AS(m_vup->dtypep()->skipRefp(), AssocArrayDType);
2130         UASSERT_OBJ(vdtypep, nodep, "ConsAssoc requires assoc upper parent data type");
2131         if (m_vup->prelim()) {
2132             nodep->dtypeFrom(vdtypep);
2133             if (nodep->defaultp()) {
2134                 iterateCheck(nodep, "default", nodep->defaultp(), CONTEXT, FINAL,
2135                              vdtypep->subDTypep(), EXTEND_EXP);
2136             }
2137         }
2138     }
visit(AstSetAssoc * nodep)2139     virtual void visit(AstSetAssoc* nodep) override {
2140         // Type computed when constructed here
2141         auto* const vdtypep = VN_AS(m_vup->dtypep()->skipRefp(), AssocArrayDType);
2142         UASSERT_OBJ(vdtypep, nodep, "SetsAssoc requires assoc upper parent data type");
2143         if (m_vup->prelim()) {
2144             nodep->dtypeFrom(vdtypep);
2145             userIterateAndNext(nodep->lhsp(), WidthVP(vdtypep, BOTH).p());
2146             iterateCheck(nodep, "key", nodep->keyp(), CONTEXT, FINAL, vdtypep->keyDTypep(),
2147                          EXTEND_EXP);
2148             iterateCheck(nodep, "value", nodep->valuep(), CONTEXT, FINAL, vdtypep->subDTypep(),
2149                          EXTEND_EXP);
2150         }
2151     }
visit(AstConsDynArray * nodep)2152     virtual void visit(AstConsDynArray* nodep) override {
2153         // Type computed when constructed here
2154         AstDynArrayDType* const vdtypep = VN_AS(m_vup->dtypep()->skipRefp(), DynArrayDType);
2155         UASSERT_OBJ(vdtypep, nodep, "ConsDynArray requires queue upper parent data type");
2156         if (m_vup->prelim()) {
2157             userIterateAndNext(nodep->lhsp(), WidthVP(vdtypep, PRELIM).p());
2158             userIterateAndNext(nodep->rhsp(), WidthVP(vdtypep, PRELIM).p());
2159             nodep->dtypeFrom(vdtypep);
2160         }
2161         if (m_vup->final()) {
2162             // Arguments can be either elements of the queue or a queue itself
2163             // Concats (part of tree of concats) must always become ConsDynArray's
2164             if (nodep->lhsp()) {
2165                 if (VN_IS(nodep->lhsp()->dtypep(), DynArrayDType)
2166                     || VN_IS(nodep->lhsp(), ConsDynArray)) {
2167                     userIterateAndNext(nodep->lhsp(), WidthVP(vdtypep, FINAL).p());
2168                 } else {
2169                     // Sub elements are not queues, but concats, must always pass concats down
2170                     iterateCheckTyped(nodep, "LHS", nodep->lhsp(), vdtypep->subDTypep(), FINAL);
2171                 }
2172             }
2173             if (nodep->rhsp()) {
2174                 if (VN_IS(nodep->rhsp()->dtypep(), DynArrayDType)
2175                     || VN_IS(nodep->rhsp(), ConsDynArray)) {
2176                     userIterateAndNext(nodep->rhsp(), WidthVP(vdtypep, FINAL).p());
2177                 } else {
2178                     iterateCheckTyped(nodep, "RHS", nodep->rhsp(), vdtypep->subDTypep(), FINAL);
2179                 }
2180             }
2181             nodep->dtypeFrom(vdtypep);
2182         }
2183     }
visit(AstConsQueue * nodep)2184     virtual void visit(AstConsQueue* nodep) override {
2185         // Type computed when constructed here
2186         AstQueueDType* const vdtypep = VN_AS(m_vup->dtypep()->skipRefp(), QueueDType);
2187         UASSERT_OBJ(vdtypep, nodep, "ConsQueue requires queue upper parent data type");
2188         if (m_vup->prelim()) {
2189             userIterateAndNext(nodep->lhsp(), WidthVP(vdtypep, PRELIM).p());
2190             userIterateAndNext(nodep->rhsp(), WidthVP(vdtypep, PRELIM).p());
2191             nodep->dtypeFrom(vdtypep);
2192         }
2193         if (m_vup->final()) {
2194             // Arguments can be either elements of the queue or a queue itself
2195             // Concats (part of tree of concats) must always become ConsQueue's
2196             if (nodep->lhsp()) {
2197                 if (VN_IS(nodep->lhsp()->dtypep(), QueueDType)
2198                     || VN_IS(nodep->lhsp(), ConsQueue)) {
2199                     userIterateAndNext(nodep->lhsp(), WidthVP(vdtypep, FINAL).p());
2200                 } else {
2201                     // Sub elements are not queues, but concats, must always pass concats down
2202                     iterateCheckTyped(nodep, "LHS", nodep->lhsp(), vdtypep->subDTypep(), FINAL);
2203                 }
2204             }
2205             if (nodep->rhsp()) {
2206                 if (VN_IS(nodep->rhsp()->dtypep(), QueueDType)
2207                     || VN_IS(nodep->rhsp(), ConsQueue)) {
2208                     userIterateAndNext(nodep->rhsp(), WidthVP(vdtypep, FINAL).p());
2209                 } else {
2210                     iterateCheckTyped(nodep, "RHS", nodep->rhsp(), vdtypep->subDTypep(), FINAL);
2211                 }
2212             }
2213             nodep->dtypeFrom(vdtypep);
2214         }
2215     }
visit(AstInitItem * nodep)2216     virtual void visit(AstInitItem* nodep) override {  //
2217         userIterateChildren(nodep, m_vup);
2218     }
visit(AstInitArray * nodep)2219     virtual void visit(AstInitArray* nodep) override {
2220         // InitArray has type of the array; children are array values
2221         if (m_vup->prelim()) {  // First stage evaluation
2222             AstNodeDType* const vdtypep = m_vup->dtypeNullp();
2223             UASSERT_OBJ(vdtypep, nodep, "InitArray type not assigned by AstPattern/Var visitor");
2224             nodep->dtypep(vdtypep);
2225             if (const AstNodeArrayDType* const arrayp
2226                 = VN_CAST(vdtypep->skipRefp(), NodeArrayDType)) {
2227                 userIterateChildren(nodep, WidthVP(arrayp->subDTypep(), BOTH).p());
2228             } else {
2229                 UINFO(1, "dtype object " << vdtypep->skipRefp() << endl);
2230                 nodep->v3fatalSrc("InitArray on non-array");
2231             }
2232         }
2233     }
visit(AstInside * nodep)2234     virtual void visit(AstInside* nodep) override {
2235         userIterateAndNext(nodep->exprp(), WidthVP(CONTEXT, PRELIM).p());
2236         for (AstNode *nextip, *itemp = nodep->itemsp(); itemp; itemp = nextip) {
2237             nextip = itemp->nextp();  // Prelim may cause the node to get replaced
2238             VL_DO_DANGLING(userIterate(itemp, WidthVP(CONTEXT, PRELIM).p()), itemp);
2239         }
2240         // Take width as maximum across all items
2241         int width = nodep->exprp()->width();
2242         int mwidth = nodep->exprp()->widthMin();
2243         for (const AstNode* itemp = nodep->itemsp(); itemp; itemp = itemp->nextp()) {
2244             width = std::max(width, itemp->width());
2245             mwidth = std::max(mwidth, itemp->widthMin());
2246         }
2247         // Apply width
2248         AstNodeDType* const subDTypep
2249             = nodep->findLogicDType(width, mwidth, nodep->exprp()->dtypep()->numeric());
2250         iterateCheck(nodep, "Inside expression", nodep->exprp(), CONTEXT, FINAL, subDTypep,
2251                      EXTEND_EXP);
2252         for (AstNode* itemp = nodep->itemsp(); itemp; itemp = itemp->nextp()) {
2253             iterateCheck(nodep, "Inside Item", itemp, CONTEXT, FINAL, subDTypep, EXTEND_EXP);
2254         }
2255         nodep->dtypeSetBit();
2256         if (debug() >= 9) nodep->dumpTree(cout, "-inside-in: ");
2257         // Now rip out the inside and replace with simple math
2258         AstNode* newp = nullptr;
2259         for (AstNode *nextip, *itemp = nodep->itemsp(); itemp; itemp = nextip) {
2260             nextip = itemp->nextp();  // Will be unlinking
2261             AstNode* inewp;
2262             const AstNodeDType* const itemDtp = itemp->dtypep()->skipRefp();
2263             if (AstInsideRange* const irangep = VN_CAST(itemp, InsideRange)) {
2264                 // Similar logic in V3Case
2265                 inewp = irangep->newAndFromInside(nodep->exprp(), irangep->lhsp()->unlinkFrBack(),
2266                                                   irangep->rhsp()->unlinkFrBack());
2267             } else if (VN_IS(itemDtp, UnpackArrayDType)) {
2268                 nodep->v3error("Unsupported: inside (set membership operator) on unpacked array");
2269                 // Need the AstInside type to persist, then
2270                 // for parameters, need V3Simulate support.
2271                 // For non-parameters, need runtime support.
2272                 continue;
2273             } else if (VN_IS(itemDtp, AssocArrayDType) || VN_IS(itemDtp, DynArrayDType)
2274                        || VN_IS(itemDtp, QueueDType)) {
2275                 nodep->v3error(
2276                     "Inside operator not legal on non-unpacked arrays (IEEE 1800-2017 11.4.13)");
2277                 continue;
2278             } else {
2279                 inewp = new AstEqWild(itemp->fileline(), nodep->exprp()->cloneTree(true),
2280                                       itemp->unlinkFrBack());
2281             }
2282             if (newp) {
2283                 newp = new AstOr(nodep->fileline(), newp, inewp);
2284             } else {
2285                 newp = inewp;
2286             }
2287         }
2288         if (!newp) newp = new AstConst(nodep->fileline(), AstConst::BitFalse());
2289         if (debug() >= 9) newp->dumpTree(cout, "-inside-out: ");
2290         nodep->replaceWith(newp);
2291         VL_DO_DANGLING(pushDeletep(nodep), nodep);
2292     }
visit(AstInsideRange * nodep)2293     virtual void visit(AstInsideRange* nodep) override {
2294         // Just do each side; AstInside will rip these nodes out later
2295         userIterateAndNext(nodep->lhsp(), m_vup);
2296         userIterateAndNext(nodep->rhsp(), m_vup);
2297         nodep->dtypeFrom(nodep->lhsp());
2298     }
2299 
visit(AstIfaceRefDType * nodep)2300     virtual void visit(AstIfaceRefDType* nodep) override {
2301         if (nodep->didWidthAndSet()) return;  // This node is a dtype & not both PRELIMed+FINALed
2302         UINFO(5, "   IFACEREF " << nodep << endl);
2303         userIterateChildren(nodep, m_vup);
2304         nodep->dtypep(nodep);
2305         nodep->widthForce(1, 1);  // Not really relevant
2306         UINFO(4, "dtWidthed " << nodep << endl);
2307     }
visit(AstNodeUOrStructDType * nodep)2308     virtual void visit(AstNodeUOrStructDType* nodep) override {
2309         if (nodep->didWidthAndSet()) return;  // This node is a dtype & not both PRELIMed+FINALed
2310         UINFO(5, "   NODECLASS " << nodep << endl);
2311         // if (debug() >= 9) nodep->dumpTree("-class-in--");
2312         if (!nodep->packed()) {
2313             nodep->v3warn(UNPACKED, "Unsupported: Unpacked struct/union");
2314             if (!v3Global.opt.structsPacked()) {
2315                 nodep->v3warn(UNPACKED, "Unsupported: --no-structs-packed");
2316             }
2317         }
2318         userIterateChildren(nodep, nullptr);  // First size all members
2319         nodep->repairMemberCache();
2320         // Determine bit assignments and width
2321         nodep->dtypep(nodep);
2322         int lsb = 0;
2323         int width = 0;
2324         nodep->isFourstate(false);
2325         // MSB is first, so go backwards
2326         AstMemberDType* itemp;
2327         for (itemp = nodep->membersp(); itemp && itemp->nextp();
2328              itemp = VN_AS(itemp->nextp(), MemberDType)) {}
2329         for (AstMemberDType* backip; itemp; itemp = backip) {
2330             if (itemp->isFourstate()) nodep->isFourstate(true);
2331             backip = VN_CAST(itemp->backp(), MemberDType);
2332             itemp->lsb(lsb);
2333             if (VN_IS(nodep, UnionDType)) {
2334                 width = std::max(width, itemp->width());
2335             } else {
2336                 lsb += itemp->width();
2337                 width += itemp->width();
2338             }
2339         }
2340         nodep->widthForce(width, width);  // Signing stays as-is, as parsed from declaration
2341         // if (debug() >= 9) nodep->dumpTree("-class-out-");
2342     }
visit(AstClass * nodep)2343     virtual void visit(AstClass* nodep) override {
2344         if (nodep->didWidthAndSet()) return;
2345         // Must do extends first, as we may in functions under this class
2346         // start following a tree of extends that takes us to other classes
2347         userIterateAndNext(nodep->extendsp(), nullptr);
2348         userIterateChildren(nodep, nullptr);  // First size all members
2349         nodep->repairCache();
2350     }
visit(AstClassRefDType * nodep)2351     virtual void visit(AstClassRefDType* nodep) override {
2352         if (nodep->didWidthAndSet()) return;
2353         // TODO this maybe eventually required to properly resolve members,
2354         // though causes problems with t_class_forward.v, so for now avoided
2355         // userIterateChildren(nodep->classp(), nullptr);
2356     }
visit(AstClassExtends * nodep)2357     virtual void visit(AstClassExtends* nodep) override {
2358         if (nodep->didWidthAndSet()) return;
2359         if (VN_IS(nodep->childDTypep(), ClassRefDType)) {
2360             nodep->dtypep(iterateEditMoveDTypep(nodep, nodep->childDTypep()));
2361         }
2362     }
visit(AstMemberDType * nodep)2363     virtual void visit(AstMemberDType* nodep) override {
2364         if (nodep->didWidthAndSet()) return;  // This node is a dtype & not both PRELIMed+FINALed
2365         // Iterate into subDTypep() to resolve that type and update pointer.
2366         nodep->refDTypep(iterateEditMoveDTypep(nodep, nodep->subDTypep()));
2367         nodep->dtypep(nodep);  // The member itself, not subDtype
2368         nodep->widthFromSub(nodep->subDTypep());
2369     }
visit(AstMemberSel * nodep)2370     virtual void visit(AstMemberSel* nodep) override {
2371         UINFO(5, "   MEMBERSEL " << nodep << endl);
2372         if (debug() >= 9) nodep->dumpTree("-mbs-in: ");
2373         userIterateChildren(nodep, WidthVP(SELF, BOTH).p());
2374         if (debug() >= 9) nodep->dumpTree("-mbs-ic: ");
2375         // Find the fromp dtype - should be a class
2376         if (!nodep->fromp()->dtypep()) nodep->fromp()->v3fatalSrc("Unlinked data type");
2377         AstNodeDType* const fromDtp = nodep->fromp()->dtypep()->skipRefToEnump();
2378         UINFO(9, "     from dt " << fromDtp << endl);
2379         if (AstNodeUOrStructDType* const adtypep = VN_CAST(fromDtp, NodeUOrStructDType)) {
2380             if (memberSelStruct(nodep, adtypep)) return;
2381         } else if (AstClassRefDType* const adtypep = VN_CAST(fromDtp, ClassRefDType)) {
2382             if (AstNode* const foundp = memberSelClass(nodep, adtypep)) {
2383                 if (AstVar* const varp = VN_CAST(foundp, Var)) {
2384                     nodep->dtypep(foundp->dtypep());
2385                     nodep->varp(varp);
2386                     return;
2387                 }
2388                 if (AstEnumItemRef* const adfoundp = VN_CAST(foundp, EnumItemRef)) {
2389                     nodep->replaceWith(adfoundp->cloneTree(false));
2390                     return;
2391                 }
2392                 UINFO(1, "found object " << foundp << endl);
2393                 nodep->v3fatalSrc("MemberSel of non-variable\n"
2394                                   << nodep->warnContextPrimary() << '\n'
2395                                   << foundp->warnOther() << "... Location of found object\n"
2396                                   << foundp->warnContextSecondary());
2397             }
2398         } else if (VN_IS(fromDtp, EnumDType)  //
2399                    || VN_IS(fromDtp, AssocArrayDType)  //
2400                    || VN_IS(fromDtp, UnpackArrayDType)  //
2401                    || VN_IS(fromDtp, DynArrayDType)  //
2402                    || VN_IS(fromDtp, QueueDType)  //
2403                    || VN_IS(fromDtp, BasicDType)) {
2404             // Method call on enum without following parenthesis, e.g. "ENUM.next"
2405             // Convert this into a method call, and let that visitor figure out what to do next
2406             AstNode* const newp = new AstMethodCall(
2407                 nodep->fileline(), nodep->fromp()->unlinkFrBack(), nodep->name(), nullptr);
2408             nodep->replaceWith(newp);
2409             VL_DO_DANGLING(pushDeletep(nodep), nodep);
2410             userIterate(newp, m_vup);
2411             return;
2412         } else {
2413             nodep->v3error("Member selection of non-struct/union object '"
2414                            << nodep->fromp()->prettyTypeName() << "' which is a '"
2415                            << nodep->fromp()->dtypep()->prettyTypeName() << "'");
2416         }
2417         // Error handling
2418         nodep->replaceWith(new AstConst(nodep->fileline(), AstConst::BitFalse()));
2419         VL_DO_DANGLING(pushDeletep(nodep), nodep);
2420     }
memberSelClass(AstMemberSel * nodep,AstClassRefDType * adtypep)2421     AstNode* memberSelClass(AstMemberSel* nodep, AstClassRefDType* adtypep) {
2422         // Returns node if ok
2423         // No need to width-resolve the class, as it was done when we did the child
2424         AstClass* const first_classp = adtypep->classp();
2425         UASSERT_OBJ(first_classp, nodep, "Unlinked");
2426         for (AstClass* classp = first_classp; classp;) {
2427             if (AstNode* const foundp = classp->findMember(nodep->name())) return foundp;
2428             classp = classp->extendsp() ? classp->extendsp()->classp() : nullptr;
2429         }
2430         VSpellCheck speller;
2431         for (AstClass* classp = first_classp; classp;) {
2432             for (AstNode* itemp = classp->membersp(); itemp; itemp = itemp->nextp()) {
2433                 if (VN_IS(itemp, Var) || VN_IS(itemp, EnumItemRef)) {
2434                     speller.pushCandidate(itemp->prettyName());
2435                 }
2436             }
2437             classp = classp->extendsp() ? classp->extendsp()->classp() : nullptr;
2438         }
2439         const string suggest = speller.bestCandidateMsg(nodep->prettyName());
2440         nodep->v3error(
2441             "Member " << nodep->prettyNameQ() << " not found in class "
2442                       << first_classp->prettyNameQ() << "\n"
2443                       << (suggest.empty() ? "" : nodep->fileline()->warnMore() + suggest));
2444         return nullptr;  // Caller handles error
2445     }
memberSelStruct(AstMemberSel * nodep,AstNodeUOrStructDType * adtypep)2446     bool memberSelStruct(AstMemberSel* nodep, AstNodeUOrStructDType* adtypep) {
2447         // Returns true if ok
2448         if (AstMemberDType* const memberp = adtypep->findMember(nodep->name())) {
2449             if (m_attrp) {  // Looking for the base of the attribute
2450                 nodep->dtypep(memberp);
2451                 UINFO(9, "   MEMBERSEL(attr) -> " << nodep << endl);
2452                 UINFO(9, "           dt-> " << nodep->dtypep() << endl);
2453             } else {
2454                 AstSel* const newp = new AstSel(nodep->fileline(), nodep->fromp()->unlinkFrBack(),
2455                                                 memberp->lsb(), memberp->width());
2456                 // Must skip over the member to find the union; as the member may disappear later
2457                 newp->dtypep(memberp->subDTypep()->skipRefToEnump());
2458                 newp->didWidth(true);  // Don't replace dtype with basic type
2459                 UINFO(9, "   MEMBERSEL -> " << newp << endl);
2460                 UINFO(9, "           dt-> " << newp->dtypep() << endl);
2461                 nodep->replaceWith(newp);
2462                 VL_DO_DANGLING(pushDeletep(nodep), nodep);
2463                 // Should be able to treat it as a normal-ish nodesel - maybe.
2464                 // The lhsp() will be strange until this stage; create the number here?
2465             }
2466             return true;
2467         }
2468         nodep->v3error("Member " << nodep->prettyNameQ() << " not found in structure");
2469         return false;
2470     }
2471 
visit(AstCMethodHard * nodep)2472     virtual void visit(AstCMethodHard* nodep) override {
2473         // Never created before V3Width, so no need to redo it
2474         UASSERT_OBJ(nodep->dtypep(), nodep, "CMETHODCALLs should have already been sized");
2475     }
2476 
visit(AstMethodCall * nodep)2477     virtual void visit(AstMethodCall* nodep) override {
2478         UINFO(5, "   METHODCALL " << nodep << endl);
2479         if (nodep->didWidth()) return;
2480         if (debug() >= 9) nodep->dumpTree("-mts-in: ");
2481         // Should check types the method requires, but at present we don't do much
2482         userIterate(nodep->fromp(), WidthVP(SELF, BOTH).p());
2483         // Any AstWith is checked later when know types, in methodWithArgument
2484         for (AstArg* argp = VN_CAST(nodep->pinsp(), Arg); argp; argp = VN_AS(argp->nextp(), Arg)) {
2485             if (argp->exprp()) userIterate(argp->exprp(), WidthVP(SELF, BOTH).p());
2486         }
2487         // Find the fromp dtype - should be a class
2488         UASSERT_OBJ(nodep->fromp() && nodep->fromp()->dtypep(), nodep, "Unsized expression");
2489         AstNodeDType* const fromDtp = nodep->fromp()->dtypep()->skipRefToEnump();
2490         AstBasicDType* const basicp = fromDtp ? fromDtp->basicp() : nullptr;
2491         UINFO(9, "     from dt " << fromDtp << endl);
2492         userIterate(fromDtp, WidthVP(SELF, BOTH).p());
2493         if (AstEnumDType* const adtypep = VN_CAST(fromDtp, EnumDType)) {
2494             methodCallEnum(nodep, adtypep);
2495         } else if (AstAssocArrayDType* const adtypep = VN_CAST(fromDtp, AssocArrayDType)) {
2496             methodCallAssoc(nodep, adtypep);
2497         } else if (AstDynArrayDType* const adtypep = VN_CAST(fromDtp, DynArrayDType)) {
2498             methodCallDyn(nodep, adtypep);
2499         } else if (AstQueueDType* const adtypep = VN_CAST(fromDtp, QueueDType)) {
2500             methodCallQueue(nodep, adtypep);
2501         } else if (AstClassRefDType* const adtypep = VN_CAST(fromDtp, ClassRefDType)) {
2502             methodCallClass(nodep, adtypep);
2503         } else if (AstUnpackArrayDType* const adtypep = VN_CAST(fromDtp, UnpackArrayDType)) {
2504             methodCallUnpack(nodep, adtypep);
2505         } else if (basicp && basicp->isEventValue()) {
2506             methodCallEvent(nodep, basicp);
2507         } else if (basicp && basicp->isString()) {
2508             methodCallString(nodep, basicp);
2509         } else {
2510             nodep->v3warn(E_UNSUPPORTED, "Unsupported: Member call on object '"
2511                                              << nodep->fromp()->prettyTypeName()
2512                                              << "' which is a '"
2513                                              << nodep->fromp()->dtypep()->prettyTypeName() << "'");
2514         }
2515     }
methodWithArgument(AstMethodCall * nodep,bool required,bool arbReturn,AstNodeDType * returnDtp,AstNodeDType * indexDtp,AstNodeDType * valueDtp)2516     AstWith* methodWithArgument(AstMethodCall* nodep, bool required, bool arbReturn,
2517                                 AstNodeDType* returnDtp, AstNodeDType* indexDtp,
2518                                 AstNodeDType* valueDtp) {
2519         UASSERT_OBJ(arbReturn || returnDtp, nodep, "Null return type");
2520         if (AstWith* const withp = VN_CAST(nodep->pinsp(), With)) {
2521             withp->indexArgRefp()->dtypep(indexDtp);
2522             withp->valueArgRefp()->dtypep(valueDtp);
2523             userIterate(withp, WidthVP(returnDtp, BOTH).p());
2524             withp->unlinkFrBack();
2525             return withp;
2526         } else if (required) {
2527             nodep->v3error("'with' statement is required for ." << nodep->prettyName()
2528                                                                 << " method");
2529         }
2530         return nullptr;
2531     }
methodOkArguments(AstMethodCall * nodep,int minArg,int maxArg)2532     void methodOkArguments(AstMethodCall* nodep, int minArg, int maxArg) {
2533         int narg = 0;
2534         for (AstNode* argp = nodep->pinsp(); argp; argp = argp->nextp()) {
2535             if (VN_IS(argp, With)) {
2536                 argp->v3error("'with' not legal on this method");
2537                 // Delete all arguments as nextp() otherwise dangling
2538                 VL_DO_DANGLING(pushDeletep(argp->unlinkFrBackWithNext()), argp);
2539                 break;
2540             }
2541             ++narg;
2542             UASSERT_OBJ(VN_IS(argp, Arg), nodep, "Method arg without Arg type");
2543         }
2544         const bool ok = (narg >= minArg) && (narg <= maxArg);
2545         if (!ok) {
2546             nodep->v3error("The " << narg << " arguments passed to ." << nodep->prettyName()
2547                                   << " method does not match its requiring " << cvtToStr(minArg)
2548                                   << (minArg == maxArg ? "" : " to " + cvtToStr(maxArg))
2549                                   << " arguments");
2550             // Adjust to required argument counts, very bogus, but avoids core dump
2551             for (; narg < minArg; ++narg) {
2552                 nodep->addPinsp(
2553                     new AstArg(nodep->fileline(), "", new AstConst(nodep->fileline(), 0)));
2554             }
2555             for (; narg > maxArg; --narg) {
2556                 AstNode* argp = nodep->pinsp();
2557                 while (argp->nextp()) argp = argp->nextp();
2558                 argp->unlinkFrBack();
2559                 VL_DO_DANGLING(argp->deleteTree(), argp);
2560             }
2561         }
2562     }
2563 
methodCallEnum(AstMethodCall * nodep,AstEnumDType * adtypep)2564     void methodCallEnum(AstMethodCall* nodep, AstEnumDType* adtypep) {
2565         // Method call on enum without following parenthesis, e.g. "ENUM.next"
2566         // Convert this into a method call, and let that visitor figure out what to do next
2567         if (adtypep) {}
2568         if (nodep->name() == "num"  //
2569             || nodep->name() == "first"  //
2570             || nodep->name() == "last") {
2571             // Constant value
2572             AstConst* newp = nullptr;
2573             methodOkArguments(nodep, 0, 0);
2574             if (nodep->name() == "num") {
2575                 int items = 0;
2576                 for (AstNode* itemp = adtypep->itemsp(); itemp; itemp = itemp->nextp()) ++items;
2577                 newp = new AstConst(nodep->fileline(), AstConst::Signed32(), items);
2578             } else if (nodep->name() == "first") {
2579                 const AstEnumItem* itemp = adtypep->itemsp();
2580                 if (!itemp) {
2581                     newp = new AstConst(nodep->fileline(), AstConst::Signed32(),
2582                                         0);  // Spec doesn't say what to do
2583                 } else {
2584                     newp = VN_AS(itemp->valuep()->cloneTree(false), Const);  // A const
2585                 }
2586             } else if (nodep->name() == "last") {
2587                 const AstEnumItem* itemp = adtypep->itemsp();
2588                 while (itemp && itemp->nextp()) itemp = VN_AS(itemp->nextp(), EnumItem);
2589                 if (!itemp) {
2590                     newp = new AstConst(nodep->fileline(), AstConst::Signed32(),
2591                                         0);  // Spec doesn't say what to do
2592                 } else {
2593                     newp = VN_AS(itemp->valuep()->cloneTree(false), Const);  // A const
2594                 }
2595             }
2596             UASSERT_OBJ(newp, nodep, "Enum method (perhaps enum item) not const");
2597             newp->fileline(nodep->fileline());  // Use method's filename/line number to be clearer;
2598                                                 // may have warning disables
2599             nodep->replaceWith(newp);
2600             VL_DO_DANGLING(pushDeletep(nodep), nodep);
2601         } else if (nodep->name() == "name" || nodep->name() == "next" || nodep->name() == "prev") {
2602             AstAttrType attrType;
2603             if (nodep->name() == "name") {
2604                 attrType = AstAttrType::ENUM_NAME;
2605             } else if (nodep->name() == "next") {
2606                 attrType = AstAttrType::ENUM_NEXT;
2607             } else if (nodep->name() == "prev") {
2608                 attrType = AstAttrType::ENUM_PREV;
2609             } else {
2610                 nodep->v3fatalSrc("Bad case");
2611             }
2612 
2613             if (nodep->name() == "name") {
2614                 methodOkArguments(nodep, 0, 0);
2615             } else if (nodep->pinsp() && !(VN_IS(VN_AS(nodep->pinsp(), Arg)->exprp(), Const))) {
2616                 nodep->pinsp()->v3fatalSrc("Unsupported: enum next/prev with non-const argument");
2617             } else if (nodep->pinsp()
2618                        && !(VN_IS(VN_AS(nodep->pinsp(), Arg)->exprp(), Const)
2619                             && VN_AS(VN_AS(nodep->pinsp(), Arg)->exprp(), Const)->toUInt() == 1
2620                             && !nodep->pinsp()->nextp())) {
2621                 // Unroll of enumVar.next(k) to enumVar.next(1).next(k - 1)
2622                 AstMethodCall* const clonep = nodep->cloneTree(false);
2623                 VN_AS(VN_AS(clonep->pinsp(), Arg)->exprp(), Const)->num().setLong(1);
2624                 const uint32_t stepWidth
2625                     = VN_AS(VN_AS(nodep->pinsp(), Arg)->exprp(), Const)->toUInt();
2626                 AstConst* const constp = new AstConst(nodep->fileline(), stepWidth - 1);
2627                 AstArg* const argp = new AstArg(nodep->fileline(), "", constp);
2628                 AstMethodCall* const newp
2629                     = new AstMethodCall(nodep->fileline(), clonep, nodep->name(), argp);
2630                 nodep->replaceWith(newp);
2631                 VL_DO_DANGLING(nodep->deleteTree(), nodep);
2632                 return;
2633             }
2634             // Need a runtime lookup table.  Yuk.
2635             const uint64_t msbdim = enumMaxValue(nodep, adtypep);
2636             const int selwidth = V3Number::log2b(msbdim) + 1;  // Width to address a bit
2637             AstVar* const varp = enumVarp(adtypep, attrType, (1ULL << selwidth) - 1);
2638             AstVarRef* const varrefp = new AstVarRef(nodep->fileline(), varp, VAccess::READ);
2639             varrefp->classOrPackagep(v3Global.rootp()->dollarUnitPkgAddp());
2640             AstNode* const newp = new AstArraySel(
2641                 nodep->fileline(), varrefp,
2642                 // Select in case widths are off due to msblen!=width
2643                 // We return "random" values if outside the range, which is fine
2644                 // as next/previous on illegal values just need something good out
2645                 new AstSel(nodep->fileline(), nodep->fromp()->unlinkFrBack(), 0, selwidth));
2646             nodep->replaceWith(newp);
2647             VL_DO_DANGLING(nodep->deleteTree(), nodep);
2648         } else {
2649             nodep->v3error("Unknown built-in enum method " << nodep->prettyNameQ());
2650         }
2651     }
methodCallAssoc(AstMethodCall * nodep,AstAssocArrayDType * adtypep)2652     void methodCallAssoc(AstMethodCall* nodep, AstAssocArrayDType* adtypep) {
2653         AstCMethodHard* newp = nullptr;
2654         if (nodep->name() == "num"  // function int num()
2655             || nodep->name() == "size") {
2656             methodOkArguments(nodep, 0, 0);
2657             newp = new AstCMethodHard(nodep->fileline(), nodep->fromp()->unlinkFrBack(), "size",
2658                                       nullptr);  // So don't need num()
2659             newp->dtypeSetSigned32();
2660         } else if (nodep->name() == "first"  // function int first(ref index)
2661                    || nodep->name() == "last"  //
2662                    || nodep->name() == "next"  //
2663                    || nodep->name() == "prev") {
2664             methodOkArguments(nodep, 1, 1);
2665             AstNode* const index_exprp = methodCallAssocIndexExpr(nodep, adtypep);
2666             newp = new AstCMethodHard(nodep->fileline(), nodep->fromp()->unlinkFrBack(),
2667                                       nodep->name(),  // first/last/next/prev
2668                                       index_exprp->unlinkFrBack());
2669             newp->dtypeSetSigned32();
2670         } else if (nodep->name() == "exists") {  // function int exists(input index)
2671             // IEEE really should have made this a "bit" return
2672             methodOkArguments(nodep, 1, 1);
2673             AstNode* const index_exprp = methodCallAssocIndexExpr(nodep, adtypep);
2674             newp = new AstCMethodHard(nodep->fileline(), nodep->fromp()->unlinkFrBack(), "exists",
2675                                       index_exprp->unlinkFrBack());
2676             newp->dtypeSetSigned32();
2677             newp->pure(true);
2678         } else if (nodep->name() == "delete") {  // function void delete([input integer index])
2679             methodOkArguments(nodep, 0, 1);
2680             methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::WRITE);
2681             if (!nodep->pinsp()) {
2682                 newp = new AstCMethodHard(nodep->fileline(), nodep->fromp()->unlinkFrBack(),
2683                                           "clear", nullptr);
2684                 newp->makeStatement();
2685             } else {
2686                 AstNode* const index_exprp = methodCallAssocIndexExpr(nodep, adtypep);
2687                 newp = new AstCMethodHard(nodep->fileline(), nodep->fromp()->unlinkFrBack(),
2688                                           "erase", index_exprp->unlinkFrBack());
2689                 newp->makeStatement();
2690             }
2691         } else if (nodep->name() == "sort" || nodep->name() == "rsort"
2692                    || nodep->name() == "reverse" || nodep->name() == "shuffle") {
2693             nodep->v3error("Array method " << nodep->prettyNameQ()
2694                                            << " not legal on associative arrays");
2695         } else if (nodep->name() == "and" || nodep->name() == "or" || nodep->name() == "xor"
2696                    || nodep->name() == "sum" || nodep->name() == "product") {
2697             // All value return
2698             AstWith* const withp = methodWithArgument(nodep, false, false, adtypep->subDTypep(),
2699                                                       adtypep->keyDTypep(), adtypep->subDTypep());
2700             methodOkArguments(nodep, 0, 0);
2701             methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::READ);
2702             newp = new AstCMethodHard(nodep->fileline(), nodep->fromp()->unlinkFrBack(),
2703                                       "r_" + nodep->name(), withp);
2704             newp->dtypeFrom(adtypep->subDTypep());
2705             if (!nodep->firstAbovep()) newp->makeStatement();
2706         } else if (nodep->name() == "min" || nodep->name() == "max" || nodep->name() == "unique"
2707                    || nodep->name() == "unique_index") {
2708             methodOkArguments(nodep, 0, 0);
2709             methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::READ);
2710             newp = new AstCMethodHard(nodep->fileline(), nodep->fromp()->unlinkFrBack(),
2711                                       nodep->name(), nullptr);
2712             if (nodep->name() == "unique_index") {
2713                 newp->dtypep(queueDTypeIndexedBy(adtypep->keyDTypep()));
2714             } else {
2715                 newp->dtypeFrom(adtypep);
2716             }
2717             if (!nodep->firstAbovep()) newp->makeStatement();
2718         } else if (nodep->name() == "find" || nodep->name() == "find_first"
2719                    || nodep->name() == "find_last") {
2720             AstWith* const withp = methodWithArgument(nodep, true, false, nodep->findBitDType(),
2721                                                       adtypep->keyDTypep(), adtypep->subDTypep());
2722             methodOkArguments(nodep, 0, 0);
2723             methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::READ);
2724             newp = new AstCMethodHard(nodep->fileline(), nodep->fromp()->unlinkFrBack(),
2725                                       nodep->name(), withp);
2726             newp->dtypeFrom(adtypep);
2727             if (!nodep->firstAbovep()) newp->makeStatement();
2728         } else if (nodep->name() == "find_index" || nodep->name() == "find_first_index"
2729                    || nodep->name() == "find_last_index") {
2730             AstWith* const withp = methodWithArgument(nodep, true, false, nodep->findBitDType(),
2731                                                       adtypep->keyDTypep(), adtypep->subDTypep());
2732             methodOkArguments(nodep, 0, 0);
2733             methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::READ);
2734             newp = new AstCMethodHard(nodep->fileline(), nodep->fromp()->unlinkFrBack(),
2735                                       nodep->name(), withp);
2736             newp->dtypep(queueDTypeIndexedBy(adtypep->keyDTypep()));
2737             if (!nodep->firstAbovep()) newp->makeStatement();
2738         } else {
2739             nodep->v3error("Unknown built-in associative array method " << nodep->prettyNameQ());
2740             nodep->dtypeFrom(adtypep->subDTypep());  // Best guess
2741         }
2742         if (newp) {
2743             newp->protect(false);
2744             newp->didWidth(true);
2745             nodep->replaceWith(newp);
2746             VL_DO_DANGLING(nodep->deleteTree(), nodep);
2747         }
2748     }
methodCallAssocIndexExpr(AstMethodCall * nodep,AstAssocArrayDType * adtypep)2749     AstNode* methodCallAssocIndexExpr(AstMethodCall* nodep, AstAssocArrayDType* adtypep) {
2750         AstNode* const index_exprp = VN_CAST(nodep->pinsp(), Arg)->exprp();
2751         iterateCheck(nodep, "index", index_exprp, CONTEXT, FINAL, adtypep->keyDTypep(),
2752                      EXTEND_EXP);
2753         VL_DANGLING(index_exprp);  // May have been edited
2754         return VN_AS(nodep->pinsp(), Arg)->exprp();
2755     }
methodCallLValueRecurse(AstMethodCall * nodep,AstNode * childp,const VAccess & access)2756     void methodCallLValueRecurse(AstMethodCall* nodep, AstNode* childp, const VAccess& access) {
2757         if (AstNodeVarRef* const varrefp = VN_CAST(childp, NodeVarRef)) {
2758             varrefp->access(access);
2759         } else if (const AstMemberSel* const ichildp = VN_CAST(childp, MemberSel)) {
2760             methodCallLValueRecurse(nodep, ichildp->fromp(), access);
2761         } else if (const AstNodeSel* const ichildp = VN_CAST(childp, NodeSel)) {
2762             methodCallLValueRecurse(nodep, ichildp->fromp(), access);
2763         } else {
2764             UINFO(1, "    Related node: " << childp << endl);
2765             nodep->v3warn(E_UNSUPPORTED, "Unsupported: Non-variable on LHS of built-in method '"
2766                                              << nodep->prettyName() << "'");
2767         }
2768     }
methodCallDyn(AstMethodCall * nodep,AstDynArrayDType * adtypep)2769     void methodCallDyn(AstMethodCall* nodep, AstDynArrayDType* adtypep) {
2770         AstCMethodHard* newp = nullptr;
2771         if (nodep->name() == "at") {  // Created internally for []
2772             methodOkArguments(nodep, 1, 1);
2773             methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::WRITE);
2774             newp = new AstCMethodHard(nodep->fileline(), nodep->fromp()->unlinkFrBack(), "at",
2775                                       nullptr);
2776             newp->dtypeFrom(adtypep->subDTypep());
2777         } else if (nodep->name() == "size") {
2778             methodOkArguments(nodep, 0, 0);
2779             newp = new AstCMethodHard(nodep->fileline(), nodep->fromp()->unlinkFrBack(), "size",
2780                                       nullptr);
2781             newp->dtypeSetSigned32();
2782         } else if (nodep->name() == "delete") {  // function void delete()
2783             methodOkArguments(nodep, 0, 0);
2784             methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::WRITE);
2785             newp = new AstCMethodHard(nodep->fileline(), nodep->fromp()->unlinkFrBack(), "clear",
2786                                       nullptr);
2787             newp->makeStatement();
2788         } else if (nodep->name() == "and" || nodep->name() == "or" || nodep->name() == "xor"
2789                    || nodep->name() == "sum" || nodep->name() == "product") {
2790             // All value return
2791             AstWith* const withp
2792                 = methodWithArgument(nodep, false, false, adtypep->subDTypep(),
2793                                      nodep->findUInt32DType(), adtypep->subDTypep());
2794             methodOkArguments(nodep, 0, 0);
2795             methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::READ);
2796             newp = new AstCMethodHard(nodep->fileline(), nodep->fromp()->unlinkFrBack(),
2797                                       "r_" + nodep->name(), withp);
2798             newp->dtypeFrom(adtypep->subDTypep());
2799             if (!nodep->firstAbovep()) newp->makeStatement();
2800         } else if (nodep->name() == "reverse" || nodep->name() == "shuffle"
2801                    || nodep->name() == "sort" || nodep->name() == "rsort") {
2802             AstWith* withp = nullptr;
2803             if (nodep->name() == "sort" || nodep->name() == "rsort") {
2804                 withp = methodWithArgument(nodep, false, true, nullptr, nodep->findUInt32DType(),
2805                                            adtypep->subDTypep());
2806             }
2807             methodOkArguments(nodep, 0, 0);
2808             methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::WRITE);
2809             newp = new AstCMethodHard(nodep->fileline(), nodep->fromp()->unlinkFrBack(),
2810                                       nodep->name(), withp);
2811             newp->makeStatement();
2812         } else if (nodep->name() == "min" || nodep->name() == "max" || nodep->name() == "unique"
2813                    || nodep->name() == "unique_index") {
2814             methodOkArguments(nodep, 0, 0);
2815             methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::READ);
2816             newp = new AstCMethodHard(nodep->fileline(), nodep->fromp()->unlinkFrBack(),
2817                                       nodep->name(), nullptr);
2818             if (nodep->name() == "unique_index") {
2819                 newp->dtypep(newp->findQueueIndexDType());
2820             } else {
2821                 newp->dtypeFrom(adtypep);
2822             }
2823             if (!nodep->firstAbovep()) newp->makeStatement();
2824         } else if (nodep->name() == "find" || nodep->name() == "find_first"
2825                    || nodep->name() == "find_last" || nodep->name() == "find_index") {
2826             AstWith* const withp
2827                 = methodWithArgument(nodep, true, false, nodep->findBitDType(),
2828                                      nodep->findUInt32DType(), adtypep->subDTypep());
2829             methodOkArguments(nodep, 0, 0);
2830             methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::READ);
2831             newp = new AstCMethodHard(nodep->fileline(), nodep->fromp()->unlinkFrBack(),
2832                                       nodep->name(), withp);
2833             newp->dtypeFrom(adtypep);
2834             if (!nodep->firstAbovep()) newp->makeStatement();
2835         } else if (nodep->name() == "find_index" || nodep->name() == "find_first_index"
2836                    || nodep->name() == "find_last_index") {
2837             AstWith* const withp
2838                 = methodWithArgument(nodep, true, false, nodep->findBitDType(),
2839                                      nodep->findUInt32DType(), adtypep->subDTypep());
2840             methodOkArguments(nodep, 0, 0);
2841             methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::READ);
2842             newp = new AstCMethodHard(nodep->fileline(), nodep->fromp()->unlinkFrBack(),
2843                                       nodep->name(), withp);
2844             newp->dtypep(newp->findQueueIndexDType());
2845             if (!nodep->firstAbovep()) newp->makeStatement();
2846         } else {
2847             nodep->v3warn(E_UNSUPPORTED, "Unsupported/unknown built-in dynamic array method "
2848                                              << nodep->prettyNameQ());
2849             nodep->dtypeFrom(adtypep->subDTypep());  // Best guess
2850         }
2851         if (newp) {
2852             newp->protect(false);
2853             newp->didWidth(true);
2854             nodep->replaceWith(newp);
2855             VL_DO_DANGLING(nodep->deleteTree(), nodep);
2856         }
2857     }
methodCallQueue(AstMethodCall * nodep,AstQueueDType * adtypep)2858     void methodCallQueue(AstMethodCall* nodep, AstQueueDType* adtypep) {
2859         AstCMethodHard* newp = nullptr;
2860         if (nodep->name() == "at") {  // Created internally for []
2861             methodOkArguments(nodep, 1, 1);
2862             methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::WRITE);
2863             newp = new AstCMethodHard(nodep->fileline(), nodep->fromp()->unlinkFrBack(), "at",
2864                                       nullptr);
2865             newp->dtypeFrom(adtypep->subDTypep());
2866         } else if (nodep->name() == "num"  // function int num()
2867                    || nodep->name() == "size") {
2868             methodOkArguments(nodep, 0, 0);
2869             newp = new AstCMethodHard(nodep->fileline(), nodep->fromp()->unlinkFrBack(), "size",
2870                                       nullptr);
2871             newp->dtypeSetSigned32();
2872         } else if (nodep->name() == "delete") {  // function void delete([input integer index])
2873             methodOkArguments(nodep, 0, 1);
2874             methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::WRITE);
2875             if (!nodep->pinsp()) {
2876                 newp = new AstCMethodHard(nodep->fileline(), nodep->fromp()->unlinkFrBack(),
2877                                           "clear", nullptr);
2878                 newp->makeStatement();
2879             } else {
2880                 AstNode* const index_exprp = methodCallQueueIndexExpr(nodep);
2881                 if (index_exprp->isZero()) {  // delete(0) is a pop_front
2882                     newp = new AstCMethodHard(nodep->fileline(), nodep->fromp()->unlinkFrBack(),
2883                                               "pop_front", nullptr);
2884                     newp->dtypeFrom(adtypep->subDTypep());
2885                     newp->makeStatement();
2886                 } else {
2887                     newp = new AstCMethodHard(nodep->fileline(), nodep->fromp()->unlinkFrBack(),
2888                                               "erase", index_exprp->unlinkFrBack());
2889                     newp->makeStatement();
2890                 }
2891             }
2892         } else if (nodep->name() == "insert") {
2893             methodOkArguments(nodep, 2, 2);
2894             methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::WRITE);
2895             AstNode* const index_exprp = methodCallQueueIndexExpr(nodep);
2896             AstArg* const argp = VN_AS(nodep->pinsp()->nextp(), Arg);
2897             iterateCheckTyped(nodep, "insert value", argp->exprp(), adtypep->subDTypep(), BOTH);
2898             if (index_exprp->isZero()) {  // insert(0, ...) is a push_front
2899                 newp = new AstCMethodHard(nodep->fileline(), nodep->fromp()->unlinkFrBack(),
2900                                           "push_front", argp->exprp()->unlinkFrBack());
2901                 newp->makeStatement();
2902             } else {
2903                 newp = new AstCMethodHard(nodep->fileline(), nodep->fromp()->unlinkFrBack(),
2904                                           nodep->name(), index_exprp->unlinkFrBack());
2905                 newp->addPinsp(argp->exprp()->unlinkFrBack());
2906                 newp->makeStatement();
2907             }
2908         } else if (nodep->name() == "pop_front" || nodep->name() == "pop_back") {
2909             methodOkArguments(nodep, 0, 0);
2910             // Returns element, so method both consumes (reads) and modifies the queue
2911             methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::READWRITE);
2912             newp = new AstCMethodHard(nodep->fileline(), nodep->fromp()->unlinkFrBack(),
2913                                       nodep->name(), nullptr);
2914             newp->dtypeFrom(adtypep->subDTypep());
2915             if (!nodep->firstAbovep()) newp->makeStatement();
2916         } else if (nodep->name() == "push_back" || nodep->name() == "push_front") {
2917             methodOkArguments(nodep, 1, 1);
2918             methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::WRITE);
2919             AstArg* const argp = VN_AS(nodep->pinsp(), Arg);
2920             iterateCheckTyped(nodep, "push value", argp->exprp(), adtypep->subDTypep(), BOTH);
2921             newp = new AstCMethodHard(nodep->fileline(), nodep->fromp()->unlinkFrBack(),
2922                                       nodep->name(), argp->exprp()->unlinkFrBack());
2923             newp->makeStatement();
2924         } else if (nodep->name() == "and" || nodep->name() == "or" || nodep->name() == "xor"
2925                    || nodep->name() == "sum" || nodep->name() == "product") {
2926             AstWith* const withp
2927                 = methodWithArgument(nodep, false, false, adtypep->subDTypep(),
2928                                      nodep->findUInt32DType(), adtypep->subDTypep());
2929             methodOkArguments(nodep, 0, 0);
2930             methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::READ);
2931             newp = new AstCMethodHard(nodep->fileline(), nodep->fromp()->unlinkFrBack(),
2932                                       "r_" + nodep->name(), withp);
2933             newp->dtypeFrom(adtypep->subDTypep());
2934             if (!nodep->firstAbovep()) newp->makeStatement();
2935         } else if (nodep->name() == "reverse" || nodep->name() == "shuffle"
2936                    || nodep->name() == "sort" || nodep->name() == "rsort") {
2937             AstWith* withp = nullptr;
2938             if (nodep->name() == "sort" || nodep->name() == "rsort") {
2939                 withp = methodWithArgument(nodep, false, true, nullptr, nodep->findUInt32DType(),
2940                                            adtypep->subDTypep());
2941             }
2942             methodOkArguments(nodep, 0, 0);
2943             methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::WRITE);
2944             newp = new AstCMethodHard(nodep->fileline(), nodep->fromp()->unlinkFrBack(),
2945                                       nodep->name(), withp);
2946             newp->makeStatement();
2947         } else if (nodep->name() == "min" || nodep->name() == "max" || nodep->name() == "unique"
2948                    || nodep->name() == "unique_index") {
2949             methodOkArguments(nodep, 0, 0);
2950             methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::READ);
2951             newp = new AstCMethodHard(nodep->fileline(), nodep->fromp()->unlinkFrBack(),
2952                                       nodep->name(), nullptr);
2953             if (nodep->name() == "unique_index") {
2954                 newp->dtypep(newp->findQueueIndexDType());
2955             } else {
2956                 newp->dtypeFrom(adtypep);
2957             }
2958             if (!nodep->firstAbovep()) newp->makeStatement();
2959         } else if (nodep->name() == "find" || nodep->name() == "find_first"
2960                    || nodep->name() == "find_last") {
2961             AstWith* const withp
2962                 = methodWithArgument(nodep, true, false, nodep->findBitDType(),
2963                                      nodep->findUInt32DType(), adtypep->subDTypep());
2964             methodOkArguments(nodep, 0, 0);
2965             methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::READ);
2966             newp = new AstCMethodHard(nodep->fileline(), nodep->fromp()->unlinkFrBack(),
2967                                       nodep->name(), withp);
2968             newp->dtypeFrom(adtypep);
2969             if (!nodep->firstAbovep()) newp->makeStatement();
2970         } else if (nodep->name() == "find_index" || nodep->name() == "find_first_index"
2971                    || nodep->name() == "find_last_index") {
2972             AstWith* const withp
2973                 = methodWithArgument(nodep, true, false, nodep->findBitDType(),
2974                                      nodep->findUInt32DType(), adtypep->subDTypep());
2975             methodOkArguments(nodep, 0, 0);
2976             methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::READ);
2977             newp = new AstCMethodHard(nodep->fileline(), nodep->fromp()->unlinkFrBack(),
2978                                       nodep->name(), withp);
2979             newp->dtypep(newp->findQueueIndexDType());
2980             if (!nodep->firstAbovep()) newp->makeStatement();
2981         } else {
2982             nodep->v3warn(E_UNSUPPORTED,
2983                           "Unsupported/unknown built-in queue method " << nodep->prettyNameQ());
2984             nodep->dtypeFrom(adtypep->subDTypep());  // Best guess
2985         }
2986         if (newp) {
2987             newp->protect(false);
2988             newp->didWidth(true);
2989             nodep->replaceWith(newp);
2990             VL_DO_DANGLING(nodep->deleteTree(), nodep);
2991         }
2992     }
methodCallQueueIndexExpr(AstMethodCall * nodep)2993     AstNode* methodCallQueueIndexExpr(AstMethodCall* nodep) {
2994         AstNode* const index_exprp = VN_AS(nodep->pinsp(), Arg)->exprp();
2995         iterateCheckSigned32(nodep, "index", index_exprp, BOTH);
2996         VL_DANGLING(index_exprp);  // May have been edited
2997         return VN_AS(nodep->pinsp(), Arg)->exprp();
2998     }
methodCallClass(AstMethodCall * nodep,AstClassRefDType * adtypep)2999     void methodCallClass(AstMethodCall* nodep, AstClassRefDType* adtypep) {
3000         // No need to width-resolve the class, as it was done when we did the child
3001         AstClass* const first_classp = adtypep->classp();
3002         if (nodep->name() == "randomize") {
3003             v3Global.useRandomizeMethods(true);
3004             V3Randomize::newRandomizeFunc(first_classp);
3005         }
3006         UASSERT_OBJ(first_classp, nodep, "Unlinked");
3007         for (AstClass* classp = first_classp; classp;) {
3008             if (AstNodeFTask* const ftaskp
3009                 = VN_CAST(classp->findMember(nodep->name()), NodeFTask)) {
3010                 userIterate(ftaskp, nullptr);
3011                 if (ftaskp->lifetime().isStatic()) {
3012                     AstNode* argsp = nullptr;
3013                     if (nodep->pinsp()) argsp = nodep->pinsp()->unlinkFrBackWithNext();
3014                     AstNodeFTaskRef* newp = nullptr;
3015                     if (VN_IS(ftaskp, Task)) {
3016                         newp = new AstTaskRef(nodep->fileline(), ftaskp->name(), argsp);
3017                     } else {
3018                         newp = new AstFuncRef(nodep->fileline(), ftaskp->name(), argsp);
3019                     }
3020                     newp->taskp(ftaskp);
3021                     newp->classOrPackagep(classp);
3022                     nodep->replaceWith(newp);
3023                     VL_DO_DANGLING(nodep->deleteTree(), nodep);
3024                 } else {
3025                     nodep->taskp(ftaskp);
3026                     nodep->dtypeFrom(ftaskp);
3027                     nodep->classOrPackagep(classp);
3028                     if (VN_IS(ftaskp, Task)) nodep->makeStatement();
3029                 }
3030                 return;
3031             }
3032             classp = classp->extendsp() ? classp->extendsp()->classp() : nullptr;
3033         }
3034         {
3035             VSpellCheck speller;
3036             for (AstClass* classp = first_classp; classp;) {
3037                 for (AstNode* itemp = classp->membersp(); itemp; itemp = itemp->nextp()) {
3038                     if (VN_IS(itemp, NodeFTask)) speller.pushCandidate(itemp->prettyName());
3039                 }
3040                 classp = classp->extendsp() ? classp->extendsp()->classp() : nullptr;
3041             }
3042             const string suggest = speller.bestCandidateMsg(nodep->prettyName());
3043             nodep->v3error("Class method "
3044                            << nodep->prettyNameQ() << " not found in class "
3045                            << first_classp->prettyNameQ() << "\n"
3046                            << (suggest.empty() ? "" : nodep->fileline()->warnMore() + suggest));
3047         }
3048         nodep->dtypeSetSigned32();  // Guess on error
3049     }
methodCallUnpack(AstMethodCall * nodep,AstUnpackArrayDType * adtypep)3050     void methodCallUnpack(AstMethodCall* nodep, AstUnpackArrayDType* adtypep) {
3051         enum : uint8_t {
3052             UNKNOWN = 0,
3053             ARRAY_OR,
3054             ARRAY_AND,
3055             ARRAY_XOR,
3056             ARRAY_SUM,
3057             ARRAY_PRODUCT
3058         } methodId;
3059 
3060         methodId = UNKNOWN;
3061         if (nodep->name() == "or") {
3062             methodId = ARRAY_OR;
3063         } else if (nodep->name() == "and") {
3064             methodId = ARRAY_AND;
3065         } else if (nodep->name() == "xor") {
3066             methodId = ARRAY_XOR;
3067         } else if (nodep->name() == "sum") {
3068             methodId = ARRAY_SUM;
3069         } else if (nodep->name() == "product") {
3070             methodId = ARRAY_PRODUCT;
3071         }
3072 
3073         if (methodId) {
3074             methodOkArguments(nodep, 0, 0);
3075             FileLine* const fl = nodep->fileline();
3076             AstNode* newp = nullptr;
3077             for (int i = 0; i < adtypep->elementsConst(); ++i) {
3078                 AstNode* const arrayRef = nodep->fromp()->cloneTree(false);
3079                 AstNode* const selector = new AstArraySel(fl, arrayRef, i);
3080                 if (!newp) {
3081                     newp = selector;
3082                 } else {
3083                     switch (methodId) {
3084                     case ARRAY_OR: newp = new AstOr(fl, newp, selector); break;
3085                     case ARRAY_AND: newp = new AstAnd(fl, newp, selector); break;
3086                     case ARRAY_XOR: newp = new AstXor(fl, newp, selector); break;
3087                     case ARRAY_SUM: newp = new AstAdd(fl, newp, selector); break;
3088                     case ARRAY_PRODUCT: newp = new AstMul(fl, newp, selector); break;
3089                     default: nodep->v3fatalSrc("bad case");
3090                     }
3091                 }
3092             }
3093             nodep->replaceWith(newp);
3094             VL_DO_DANGLING(nodep->deleteTree(), nodep);
3095         } else {
3096             nodep->v3error("Unknown built-in array method " << nodep->prettyNameQ());
3097             nodep->dtypeFrom(adtypep->subDTypep());  // Best guess
3098         }
3099     }
methodCallEvent(AstMethodCall * nodep,AstBasicDType *)3100     void methodCallEvent(AstMethodCall* nodep, AstBasicDType*) {
3101         // Method call on event
3102         if (nodep->name() == "triggered") {
3103             // We represent events as numbers, so can just return number
3104             methodOkArguments(nodep, 0, 0);
3105             AstNode* const newp = nodep->fromp()->unlinkFrBack();
3106             nodep->replaceWith(newp);
3107             VL_DO_DANGLING(pushDeletep(nodep), nodep);
3108         } else {
3109             nodep->v3error("Unknown built-in event method " << nodep->prettyNameQ());
3110         }
3111     }
methodCallString(AstMethodCall * nodep,AstBasicDType *)3112     void methodCallString(AstMethodCall* nodep, AstBasicDType*) {
3113         // Method call on string
3114         if (nodep->name() == "len") {
3115             // Constant value
3116             methodOkArguments(nodep, 0, 0);
3117             AstNode* const newp = new AstLenN(nodep->fileline(), nodep->fromp()->unlinkFrBack());
3118             nodep->replaceWith(newp);
3119             VL_DO_DANGLING(pushDeletep(nodep), nodep);
3120         } else if (nodep->name() == "itoa") {
3121             methodOkArguments(nodep, 1, 1);
3122             VL_DO_DANGLING(replaceWithSFormat(nodep, "%0d"), nodep);
3123         } else if (nodep->name() == "hextoa") {
3124             methodOkArguments(nodep, 1, 1);
3125             VL_DO_DANGLING(replaceWithSFormat(nodep, "%0x"), nodep);
3126         } else if (nodep->name() == "octtoa") {
3127             methodOkArguments(nodep, 1, 1);
3128             VL_DO_DANGLING(replaceWithSFormat(nodep, "%0o"), nodep);
3129         } else if (nodep->name() == "bintoa") {
3130             methodOkArguments(nodep, 1, 1);
3131             VL_DO_DANGLING(replaceWithSFormat(nodep, "%0b"), nodep);
3132         } else if (nodep->name() == "realtoa") {
3133             methodOkArguments(nodep, 1, 1);
3134             VL_DO_DANGLING(replaceWithSFormat(nodep, "%g"), nodep);
3135         } else if (nodep->name() == "tolower") {
3136             methodOkArguments(nodep, 0, 0);
3137             AstNode* const newp
3138                 = new AstToLowerN(nodep->fileline(), nodep->fromp()->unlinkFrBack());
3139             nodep->replaceWith(newp);
3140             VL_DO_DANGLING(nodep->deleteTree(), nodep);
3141         } else if (nodep->name() == "toupper") {
3142             methodOkArguments(nodep, 0, 0);
3143             AstNode* const newp
3144                 = new AstToUpperN(nodep->fileline(), nodep->fromp()->unlinkFrBack());
3145             nodep->replaceWith(newp);
3146             VL_DO_DANGLING(nodep->deleteTree(), nodep);
3147         } else if (nodep->name() == "compare" || nodep->name() == "icompare") {
3148             const bool ignoreCase = nodep->name()[0] == 'i';
3149             methodOkArguments(nodep, 1, 1);
3150             AstArg* const argp = VN_AS(nodep->pinsp(), Arg);
3151             AstNode* const lhs = nodep->fromp()->unlinkFrBack();
3152             AstNode* const rhs = argp->exprp()->unlinkFrBack();
3153             AstNode* const newp = new AstCompareNN(nodep->fileline(), lhs, rhs, ignoreCase);
3154             nodep->replaceWith(newp);
3155             VL_DO_DANGLING(nodep->deleteTree(), nodep);
3156         } else if (nodep->name() == "putc") {
3157             methodOkArguments(nodep, 2, 2);
3158             AstArg* const arg0p = VN_AS(nodep->pinsp(), Arg);
3159             AstArg* const arg1p = VN_AS(arg0p->nextp(), Arg);
3160             AstNodeVarRef* const fromp = VN_AS(nodep->fromp()->unlinkFrBack(), VarRef);
3161             AstNode* const rhsp = arg0p->exprp()->unlinkFrBack();
3162             AstNode* const thsp = arg1p->exprp()->unlinkFrBack();
3163             AstVarRef* const varrefp
3164                 = new AstVarRef(nodep->fileline(), fromp->varp(), VAccess::READ);
3165             AstNode* const newp = new AstAssign(
3166                 nodep->fileline(), fromp, new AstPutcN(nodep->fileline(), varrefp, rhsp, thsp));
3167             fromp->access(VAccess::WRITE);
3168             nodep->replaceWith(newp);
3169             VL_DO_DANGLING(nodep->deleteTree(), nodep);
3170         } else if (nodep->name() == "getc") {
3171             methodOkArguments(nodep, 1, 1);
3172             AstArg* const arg0p = VN_AS(nodep->pinsp(), Arg);
3173             AstNode* const lhsp = nodep->fromp()->unlinkFrBack();
3174             AstNode* const rhsp = arg0p->exprp()->unlinkFrBack();
3175             AstNode* const newp = new AstGetcN(nodep->fileline(), lhsp, rhsp);
3176             nodep->replaceWith(newp);
3177             VL_DO_DANGLING(nodep->deleteTree(), nodep);
3178         } else if (nodep->name() == "substr") {
3179             methodOkArguments(nodep, 2, 2);
3180             AstArg* const arg0p = VN_AS(nodep->pinsp(), Arg);
3181             AstArg* const arg1p = VN_AS(arg0p->nextp(), Arg);
3182             AstNode* const lhsp = nodep->fromp()->unlinkFrBack();
3183             AstNode* const rhsp = arg0p->exprp()->unlinkFrBack();
3184             AstNode* const thsp = arg1p->exprp()->unlinkFrBack();
3185             AstNode* const newp = new AstSubstrN(nodep->fileline(), lhsp, rhsp, thsp);
3186             nodep->replaceWith(newp);
3187             VL_DO_DANGLING(nodep->deleteTree(), nodep);
3188         } else if (nodep->name() == "atobin" || nodep->name() == "atohex"
3189                    || nodep->name() == "atoi" || nodep->name() == "atooct"
3190                    || nodep->name() == "atoreal") {
3191             AstAtoN::FmtType fmt;
3192             if (nodep->name() == "atobin") {
3193                 fmt = AstAtoN::ATOBIN;
3194             } else if (nodep->name() == "atohex") {
3195                 fmt = AstAtoN::ATOHEX;
3196             } else if (nodep->name() == "atoi") {
3197                 fmt = AstAtoN::ATOI;
3198             } else if (nodep->name() == "atooct") {
3199                 fmt = AstAtoN::ATOOCT;
3200             } else if (nodep->name() == "atoreal") {
3201                 fmt = AstAtoN::ATOREAL;
3202             } else {
3203                 V3ERROR_NA;
3204                 fmt = AstAtoN::ATOI;
3205             }  // dummy assignment to suppress compiler warning
3206             methodOkArguments(nodep, 0, 0);
3207             AstNode* const newp
3208                 = new AstAtoN(nodep->fileline(), nodep->fromp()->unlinkFrBack(), fmt);
3209             nodep->replaceWith(newp);
3210             VL_DO_DANGLING(nodep->deleteTree(), nodep);
3211         } else {
3212             nodep->v3error("Unknown built-in string method " << nodep->prettyNameQ());
3213         }
3214     }
queueDTypeIndexedBy(AstNodeDType * indexDTypep)3215     AstQueueDType* queueDTypeIndexedBy(AstNodeDType* indexDTypep) {
3216         // Return a Queue data type with the specified index, remembering so can use again if
3217         // needed
3218         if (AstQueueDType* const queuep = m_queueDTypeIndexed[indexDTypep]) {
3219             return queuep;
3220         } else {
3221             auto* const newp = new AstQueueDType(indexDTypep->fileline(), indexDTypep, nullptr);
3222             v3Global.rootp()->typeTablep()->addTypesp(newp);
3223             m_queueDTypeIndexed[indexDTypep] = newp;
3224             return newp;
3225         }
3226     }
3227 
visit(AstNew * nodep)3228     virtual void visit(AstNew* nodep) override {
3229         if (nodep->didWidth()) return;
3230         AstClassRefDType* const refp
3231             = m_vup ? VN_CAST(m_vup->dtypeNullSkipRefp(), ClassRefDType) : nullptr;
3232         if (!refp) {  // e.g. int a = new;
3233             nodep->v3error("new() not expected in this context");
3234             return;
3235         }
3236         nodep->dtypep(refp);
3237 
3238         AstClass* const classp = refp->classp();
3239         UASSERT_OBJ(classp, nodep, "Unlinked");
3240         if (AstNodeFTask* const ftaskp = VN_CAST(classp->findMember("new"), Func)) {
3241             nodep->taskp(ftaskp);
3242             nodep->classOrPackagep(classp);
3243         } else {
3244             // Either made explicitly or V3LinkDot made implicitly
3245             classp->v3fatalSrc("Can't find class's new");
3246         }
3247         if (classp->isVirtual()) {
3248             nodep->v3error(
3249                 "Illegal to call 'new' using an abstract virtual class (IEEE 1800-2017 8.21)");
3250         }
3251         userIterate(nodep->taskp(), nullptr);
3252         processFTaskRefArgs(nodep);
3253     }
visit(AstNewCopy * nodep)3254     virtual void visit(AstNewCopy* nodep) override {
3255         if (nodep->didWidthAndSet()) return;
3256         AstClassRefDType* const refp = VN_CAST(m_vup->dtypeNullSkipRefp(), ClassRefDType);
3257         if (!refp) {  // e.g. int a = new;
3258             nodep->v3error("new() not expected in this context");
3259             return;
3260         }
3261         nodep->dtypep(refp);
3262         userIterateChildren(nodep, WidthVP(SELF, BOTH).p());
3263         if (!similarDTypeRecurse(nodep->dtypep(), nodep->rhsp()->dtypep())) {
3264             nodep->rhsp()->v3error("New-as-copier passed different data type '"
3265                                    << nodep->dtypep()->prettyTypeName() << "' then expected '"
3266                                    << nodep->rhsp()->dtypep()->prettyTypeName() << "'");
3267         }
3268     }
visit(AstNewDynamic * nodep)3269     virtual void visit(AstNewDynamic* nodep) override {
3270         if (nodep->didWidthAndSet()) return;
3271         AstDynArrayDType* const adtypep = VN_CAST(m_vup->dtypeNullSkipRefp(), DynArrayDType);
3272         if (!adtypep) {  // e.g. int a = new;
3273             nodep->v3error(
3274                 "dynamic new() not expected in this context (data type must be dynamic array)");
3275             return;
3276         }
3277         // The AstNodeAssign visitor will be soon be replacing this node, make sure it gets it
3278         if (!VN_IS(nodep->backp(), NodeAssign)) {
3279             if (adtypep) UINFO(1, "Got backp " << nodep->backp() << endl);
3280             nodep->v3error(
3281                 "dynamic new() not expected in this context (expected under an assign)");
3282             return;
3283         }
3284         nodep->dtypep(adtypep);
3285         if (m_vup && m_vup->prelim()) {
3286             iterateCheckSigned32(nodep, "new() size", nodep->sizep(), BOTH);
3287         }
3288         if (nodep->rhsp()) {
3289             iterateCheckTyped(nodep, "Dynamic array new RHS", nodep->rhsp(), adtypep, BOTH);
3290         }
3291     }
3292 
visit(AstPattern * nodep)3293     virtual void visit(AstPattern* nodep) override {
3294         if (nodep->didWidthAndSet()) return;
3295         UINFO(9, "PATTERN " << nodep << endl);
3296         if (nodep->childDTypep()) {  // data_type '{ pattern }
3297             nodep->dtypep(iterateEditMoveDTypep(nodep, nodep->subDTypep()));
3298         }
3299         if (!nodep->dtypep() && m_vup->dtypeNullp()) {  // Get it from parent assignment/pin/etc
3300             nodep->dtypep(m_vup->dtypep());
3301         }
3302         AstNodeDType* dtypep = nodep->dtypep();
3303         if (!dtypep) {
3304             nodep->v3warn(E_UNSUPPORTED, "Unsupported/Illegal: Assignment pattern"
3305                                          " member not underneath a supported construct: "
3306                                              << nodep->backp()->prettyTypeName());
3307             return;
3308         }
3309         {
3310             dtypep = dtypep->skipRefp();
3311             nodep->dtypep(dtypep);
3312             UINFO(9, "  dtypep " << dtypep << endl);
3313             nodep->dtypep(dtypep);
3314             // Determine replication count, and replicate initial value as
3315             // widths need to be individually determined
3316             for (AstPatMember* patp = VN_AS(nodep->itemsp(), PatMember); patp;
3317                  patp = VN_AS(patp->nextp(), PatMember)) {
3318                 const int times = visitPatMemberRep(patp);
3319                 for (int i = 1; i < times; i++) {
3320                     AstNode* const newp = patp->cloneTree(false);
3321                     patp->addNextHere(newp);
3322                     // This loop will see the new elements as part of nextp()
3323                 }
3324             }
3325             // Convert any PatMember with multiple items to multiple PatMembers
3326             for (AstPatMember* patp = VN_AS(nodep->itemsp(), PatMember); patp;
3327                  patp = VN_AS(patp->nextp(), PatMember)) {
3328                 if (patp->lhssp()->nextp()) {
3329                     // Can't just addNext, as would add to end of all members.
3330                     // So detach, add next and reattach
3331                     AstNRelinker relinkHandle;
3332                     patp->unlinkFrBack(&relinkHandle);
3333                     while (AstNode* const movep = patp->lhssp()->nextp()) {
3334                         movep->unlinkFrBack();  // Not unlinkFrBackWithNext, just one
3335                         AstNode* newkeyp = nullptr;
3336                         if (patp->keyp()) newkeyp = patp->keyp()->cloneTree(true);
3337                         AstPatMember* const newp
3338                             = new AstPatMember(patp->fileline(), movep, newkeyp, nullptr);
3339                         patp->addNext(newp);
3340                     }
3341                     relinkHandle.relink(patp);
3342                 }
3343             }
3344             AstPatMember* defaultp = nullptr;
3345             for (AstPatMember* patp = VN_AS(nodep->itemsp(), PatMember); patp;
3346                  patp = VN_AS(patp->nextp(), PatMember)) {
3347                 if (patp->isDefault()) {
3348                     if (defaultp) nodep->v3error("Multiple '{ default: } clauses");
3349                     defaultp = patp;
3350                     patp->unlinkFrBack();
3351                 }
3352             }
3353             while (const AstConstDType* const vdtypep = VN_CAST(dtypep, ConstDType)) {
3354                 dtypep = vdtypep->subDTypep()->skipRefp();
3355             }
3356 
3357             userIterate(dtypep, WidthVP(SELF, BOTH).p());
3358 
3359             if (auto* const vdtypep = VN_CAST(dtypep, NodeUOrStructDType)) {
3360                 VL_DO_DANGLING(patternUOrStruct(nodep, vdtypep, defaultp), nodep);
3361             } else if (auto* const vdtypep = VN_CAST(dtypep, NodeArrayDType)) {
3362                 VL_DO_DANGLING(patternArray(nodep, vdtypep, defaultp), nodep);
3363             } else if (auto* const vdtypep = VN_CAST(dtypep, AssocArrayDType)) {
3364                 VL_DO_DANGLING(patternAssoc(nodep, vdtypep, defaultp), nodep);
3365             } else if (auto* const vdtypep = VN_CAST(dtypep, DynArrayDType)) {
3366                 VL_DO_DANGLING(patternDynArray(nodep, vdtypep, defaultp), nodep);
3367             } else if (auto* const vdtypep = VN_CAST(dtypep, QueueDType)) {
3368                 VL_DO_DANGLING(patternQueue(nodep, vdtypep, defaultp), nodep);
3369             } else if (VN_IS(dtypep, BasicDType) && VN_AS(dtypep, BasicDType)->isRanged()) {
3370                 VL_DO_DANGLING(patternBasic(nodep, dtypep, defaultp), nodep);
3371             } else {
3372                 nodep->v3warn(
3373                     E_UNSUPPORTED,
3374                     "Unsupported: Assignment pattern applies against non struct/union data type: "
3375                         << dtypep->prettyDTypeNameQ());
3376             }
3377         }
3378     }
patternUOrStruct(AstPattern * nodep,AstNodeUOrStructDType * vdtypep,AstPatMember * defaultp)3379     void patternUOrStruct(AstPattern* nodep, AstNodeUOrStructDType* vdtypep,
3380                           AstPatMember* defaultp) {
3381         // Due to "default" and tagged patterns, we need to determine
3382         // which member each AstPatMember corresponds to before we can
3383         // determine the dtypep for that PatMember's value, and then
3384         // width the initial value appropriately.
3385         using PatMap = std::map<const AstMemberDType*, AstPatMember*>;
3386         PatMap patmap;
3387         {
3388             const AstMemberDType* memp = vdtypep->membersp();
3389             AstPatMember* patp = VN_CAST(nodep->itemsp(), PatMember);
3390             for (; memp || patp;) {
3391                 do {
3392                     if (patp) {
3393                         if (patp->keyp()) {
3394                             if (const AstText* textp = VN_CAST(patp->keyp(), Text)) {
3395                                 memp = vdtypep->findMember(textp->text());
3396                                 if (!memp) {
3397                                     patp->keyp()->v3error("Assignment pattern key '"
3398                                                           << textp->text()
3399                                                           << "' not found as member");
3400                                     break;
3401                                 }
3402                             } else {
3403                                 patp->keyp()->v3error(
3404                                     "Assignment pattern key not supported/understood: "
3405                                     << patp->keyp()->prettyTypeName());
3406                             }
3407                         }
3408                     }
3409                     if (memp && !patp) {
3410                         // Missing init elements, warn below
3411                         memp = nullptr;
3412                         patp = nullptr;
3413                         break;
3414                     } else if (!memp && patp) {
3415                         patp->v3error("Assignment pattern contains too many elements");
3416                         memp = nullptr;
3417                         patp = nullptr;
3418                         break;
3419                     } else {
3420                         const std::pair<PatMap::iterator, bool> ret = patmap.emplace(memp, patp);
3421                         if (!ret.second) {
3422                             patp->v3error("Assignment pattern contains duplicate entry: "
3423                                           << VN_AS(patp->keyp(), Text)->text());
3424                         }
3425                     }
3426                 } while (false);
3427                 // Next
3428                 if (memp) memp = VN_AS(memp->nextp(), MemberDType);
3429                 if (patp) patp = VN_AS(patp->nextp(), PatMember);
3430             }
3431         }
3432         AstNode* newp = nullptr;
3433         for (AstMemberDType* memp = vdtypep->membersp(); memp;
3434              memp = VN_AS(memp->nextp(), MemberDType)) {
3435             const auto it = patmap.find(memp);
3436             AstPatMember* newpatp = nullptr;
3437             AstPatMember* patp = nullptr;
3438             if (it == patmap.end()) {
3439                 if (defaultp) {
3440                     newpatp = defaultp->cloneTree(false);
3441                     patp = newpatp;
3442                 } else {
3443                     if (!VN_IS(vdtypep, UnionDType)) {
3444                         nodep->v3error("Assignment pattern missed initializing elements: "
3445                                        << memp->prettyTypeName());
3446                     }
3447                 }
3448             } else {
3449                 patp = it->second;
3450             }
3451             if (patp) {
3452                 // Determine initial values
3453                 patp->dtypep(memp);
3454                 AstNode* const valuep = patternMemberValueIterate(patp);
3455                 if (!newp) {
3456                     newp = valuep;
3457                 } else {
3458                     AstConcat* const concatp = new AstConcat(patp->fileline(), newp, valuep);
3459                     newp = concatp;
3460                     newp->dtypeSetLogicSized(concatp->lhsp()->width() + concatp->rhsp()->width(),
3461                                              nodep->dtypep()->numeric());
3462                 }
3463             }
3464             if (newpatp) VL_DO_DANGLING(pushDeletep(newpatp), newpatp);
3465         }
3466         if (newp) {
3467             nodep->replaceWith(newp);
3468         } else {
3469             nodep->v3error("Assignment pattern with no members");
3470         }
3471         VL_DO_DANGLING(pushDeletep(nodep), nodep);  // Deletes defaultp also, if present
3472     }
patternArray(AstPattern * nodep,AstNodeArrayDType * arrayDtp,AstPatMember * defaultp)3473     void patternArray(AstPattern* nodep, AstNodeArrayDType* arrayDtp, AstPatMember* defaultp) {
3474         const VNumRange range = arrayDtp->declRange();
3475         PatVecMap patmap = patVectorMap(nodep, range);
3476         UINFO(9, "ent " << range.left() << " to " << range.right() << endl);
3477         AstNode* newp = nullptr;
3478         for (int entn = 0, ent = range.left(); entn < range.elements();
3479              ++entn, ent += range.leftToRightInc()) {
3480             AstPatMember* newpatp = nullptr;
3481             AstPatMember* patp = nullptr;
3482             const auto it = patmap.find(ent);
3483             if (it == patmap.end()) {
3484                 if (defaultp) {
3485                     newpatp = defaultp->cloneTree(false);
3486                     patp = newpatp;
3487                 } else {
3488                     nodep->v3error("Assignment pattern missed initializing elements: " << ent);
3489                 }
3490             } else {
3491                 patp = it->second;
3492                 patmap.erase(it);
3493             }
3494 
3495             if (patp) {
3496                 // Don't want the RHS an array
3497                 patp->dtypep(arrayDtp->subDTypep());
3498                 AstNode* const valuep = patternMemberValueIterate(patp);
3499                 if (VN_IS(arrayDtp, UnpackArrayDType)) {
3500                     if (!newp) {
3501                         AstInitArray* const newap
3502                             = new AstInitArray(nodep->fileline(), arrayDtp, nullptr);
3503                         newp = newap;
3504                     }
3505                     VN_AS(newp, InitArray)->addIndexValuep(ent - range.lo(), valuep);
3506                 } else {  // Packed. Convert to concat for now.
3507                     if (!newp) {
3508                         newp = valuep;
3509                     } else {
3510                         AstConcat* const concatp = new AstConcat(patp->fileline(), newp, valuep);
3511                         newp = concatp;
3512                         newp->dtypeSetLogicSized(concatp->lhsp()->width()
3513                                                      + concatp->rhsp()->width(),
3514                                                  nodep->dtypep()->numeric());
3515                     }
3516                 }
3517             }
3518             if (newpatp) VL_DO_DANGLING(pushDeletep(newpatp), newpatp);
3519         }
3520         if (!patmap.empty()) nodep->v3error("Assignment pattern with too many elements");
3521         if (newp) {
3522             nodep->replaceWith(newp);
3523         } else {
3524             nodep->v3error("Assignment pattern with no members");
3525         }
3526         // if (debug() >= 9) newp->dumpTree("-apat-out: ");
3527         VL_DO_DANGLING(pushDeletep(nodep), nodep);  // Deletes defaultp also, if present
3528     }
patternAssoc(AstPattern * nodep,AstAssocArrayDType * arrayDtp,AstPatMember * defaultp)3529     void patternAssoc(AstPattern* nodep, AstAssocArrayDType* arrayDtp, AstPatMember* defaultp) {
3530         AstNode* defaultValuep = nullptr;
3531         if (defaultp) defaultValuep = defaultp->lhssp()->unlinkFrBack();
3532         AstNode* newp = new AstConsAssoc(nodep->fileline(), defaultValuep);
3533         newp->dtypeFrom(arrayDtp);
3534         for (AstPatMember* patp = VN_AS(nodep->itemsp(), PatMember); patp;
3535              patp = VN_AS(patp->nextp(), PatMember)) {
3536             patp->dtypep(arrayDtp->subDTypep());
3537             AstNode* const valuep = patternMemberValueIterate(patp);
3538             AstNode* const keyp = patp->keyp();
3539             auto* const newap
3540                 = new AstSetAssoc(nodep->fileline(), newp, keyp->unlinkFrBack(), valuep);
3541             newap->dtypeFrom(arrayDtp);
3542             newp = newap;
3543         }
3544         nodep->replaceWith(newp);
3545         // if (debug() >= 9) newp->dumpTree("-apat-out: ");
3546         VL_DO_DANGLING(pushDeletep(nodep), nodep);  // Deletes defaultp also, if present
3547     }
patternDynArray(AstPattern * nodep,AstDynArrayDType * arrayp,AstPatMember *)3548     void patternDynArray(AstPattern* nodep, AstDynArrayDType* arrayp, AstPatMember*) {
3549         AstNode* newp = new AstConsDynArray(nodep->fileline());
3550         newp->dtypeFrom(arrayp);
3551         for (AstPatMember* patp = VN_AS(nodep->itemsp(), PatMember); patp;
3552              patp = VN_AS(patp->nextp(), PatMember)) {
3553             patp->dtypep(arrayp->subDTypep());
3554             AstNode* const valuep = patternMemberValueIterate(patp);
3555             auto* const newap = new AstConsDynArray(nodep->fileline(), valuep, newp);
3556             newap->dtypeFrom(arrayp);
3557             newp = newap;
3558         }
3559         nodep->replaceWith(newp);
3560         // if (debug() >= 9) newp->dumpTree("-apat-out: ");
3561         VL_DO_DANGLING(pushDeletep(nodep), nodep);  // Deletes defaultp also, if present
3562     }
patternQueue(AstPattern * nodep,AstQueueDType * arrayp,AstPatMember *)3563     void patternQueue(AstPattern* nodep, AstQueueDType* arrayp, AstPatMember*) {
3564         AstNode* newp = new AstConsQueue(nodep->fileline());
3565         newp->dtypeFrom(arrayp);
3566         for (AstPatMember* patp = VN_AS(nodep->itemsp(), PatMember); patp;
3567              patp = VN_AS(patp->nextp(), PatMember)) {
3568             patp->dtypep(arrayp->subDTypep());
3569             AstNode* const valuep = patternMemberValueIterate(patp);
3570             auto* const newap = new AstConsQueue(nodep->fileline(), valuep, newp);
3571             newap->dtypeFrom(arrayp);
3572             newp = newap;
3573         }
3574         nodep->replaceWith(newp);
3575         // if (debug() >= 9) newp->dumpTree("-apat-out: ");
3576         VL_DO_DANGLING(pushDeletep(nodep), nodep);  // Deletes defaultp also, if present
3577     }
patternBasic(AstPattern * nodep,AstNodeDType * vdtypep,AstPatMember * defaultp)3578     void patternBasic(AstPattern* nodep, AstNodeDType* vdtypep, AstPatMember* defaultp) {
3579         const AstBasicDType* bdtypep = VN_AS(vdtypep, BasicDType);
3580         const VNumRange range = bdtypep->declRange();
3581         PatVecMap patmap = patVectorMap(nodep, range);
3582         UINFO(9, "ent " << range.hi() << " to " << range.lo() << endl);
3583         AstNode* newp = nullptr;
3584         for (int ent = range.hi(); ent >= range.lo(); --ent) {
3585             AstPatMember* newpatp = nullptr;
3586             AstPatMember* patp = nullptr;
3587             const auto it = patmap.find(ent);
3588             if (it == patmap.end()) {
3589                 if (defaultp) {
3590                     newpatp = defaultp->cloneTree(false);
3591                     patp = newpatp;
3592                 } else {
3593                     nodep->v3error("Assignment pattern missed initializing elements: " << ent);
3594                 }
3595             } else {
3596                 patp = it->second;
3597                 patmap.erase(it);
3598             }
3599             if (patp) {
3600                 // Determine initial values
3601                 vdtypep = nodep->findBitDType();
3602                 patp->dtypep(vdtypep);
3603                 AstNode* const valuep = patternMemberValueIterate(patp);
3604                 {  // Packed. Convert to concat for now.
3605                     if (!newp) {
3606                         newp = valuep;
3607                     } else {
3608                         AstConcat* const concatp = new AstConcat(patp->fileline(), newp, valuep);
3609                         newp = concatp;
3610                         newp->dtypeSetLogicSized(concatp->lhsp()->width()
3611                                                      + concatp->rhsp()->width(),
3612                                                  nodep->dtypep()->numeric());
3613                     }
3614                 }
3615             }
3616             if (newpatp) VL_DO_DANGLING(pushDeletep(newpatp), newpatp);
3617         }
3618         if (!patmap.empty()) nodep->v3error("Assignment pattern with too many elements");
3619         if (newp) {
3620             nodep->replaceWith(newp);
3621         } else {
3622             nodep->v3error("Assignment pattern with no members");
3623         }
3624         // if (debug() >= 9) newp->dumpTree("-apat-out: ");
3625         VL_DO_DANGLING(pushDeletep(nodep), nodep);  // Deletes defaultp also, if present
3626     }
patternMemberValueIterate(AstPatMember * patp)3627     AstNode* patternMemberValueIterate(AstPatMember* patp) {
3628         // Determine values - might be another InitArray
3629         userIterate(patp, WidthVP(patp->dtypep(), BOTH).p());
3630         // Convert to InitArray or constify immediately
3631         AstNode* valuep = patp->lhssp()->unlinkFrBack();
3632         if (VN_IS(valuep, Const)) {
3633             // Forming a AstConcat will cause problems with
3634             // unsized (uncommitted sized) constants
3635             if (AstNode* const newp
3636                 = WidthCommitVisitor::newIfConstCommitSize(VN_AS(valuep, Const))) {
3637                 VL_DO_DANGLING(pushDeletep(valuep), valuep);
3638                 valuep = newp;
3639             }
3640         }
3641         return valuep;
3642     }
3643 
visit(AstPatMember * nodep)3644     virtual void visit(AstPatMember* nodep) override {
3645         AstNodeDType* const vdtypep = m_vup->dtypeNullp();
3646         UASSERT_OBJ(vdtypep, nodep, "Pattern member type not assigned by AstPattern visitor");
3647         nodep->dtypep(vdtypep);
3648         UINFO(9, "   PATMEMBER " << nodep << endl);
3649         UASSERT_OBJ(!nodep->lhssp()->nextp(), nodep,
3650                     "PatMember value should be singular w/replicates removed");
3651         // Need to propagate assignment type downwards, even on prelim
3652         userIterateChildren(nodep, WidthVP(nodep->dtypep(), PRELIM).p());
3653         iterateCheck(nodep, "Pattern value", nodep->lhssp(), ASSIGN, FINAL, vdtypep, EXTEND_LHS);
3654     }
visitPatMemberRep(AstPatMember * nodep)3655     int visitPatMemberRep(AstPatMember* nodep) {
3656         uint32_t times = 1;
3657         if (nodep->repp()) {  // else repp()==nullptr shorthand for rep count 1
3658             iterateCheckSizedSelf(nodep, "LHS", nodep->repp(), SELF, BOTH);
3659             V3Const::constifyParamsEdit(nodep->repp());  // repp may change
3660             const AstConst* const constp = VN_CAST(nodep->repp(), Const);
3661             if (!constp) {
3662                 nodep->v3error("Replication value isn't a constant.");
3663                 times = 0;
3664             } else {
3665                 times = constp->toUInt();
3666             }
3667             if (times == 0) {
3668                 nodep->v3error("Pattern replication value of 0 is not legal.");
3669                 times = 1;
3670             }
3671             nodep->repp()
3672                 ->unlinkFrBackWithNext()
3673                 ->deleteTree();  // Done with replicate before cloning
3674         }
3675         return times;
3676     }
3677 
visit(AstPropClocked * nodep)3678     virtual void visit(AstPropClocked* nodep) override {
3679         if (m_vup->prelim()) {  // First stage evaluation
3680             iterateCheckBool(nodep, "Property", nodep->propp(), BOTH);
3681             userIterateAndNext(nodep->sensesp(), nullptr);
3682             if (nodep->disablep()) {
3683                 iterateCheckBool(nodep, "Disable", nodep->disablep(),
3684                                  BOTH);  // it's like an if() condition.
3685             }
3686             nodep->dtypeSetBit();
3687         }
3688     }
3689 
3690     //--------------------
3691     // Top levels
3692 
visit(AstNodeCase * nodep)3693     virtual void visit(AstNodeCase* nodep) override {
3694         // IEEE-2012 12.5:
3695         //    Width: MAX(expr, all items)
3696         //    Signed: Only if expr, and all items signed
3697         assertAtStatement(nodep);
3698         userIterateAndNext(nodep->exprp(), WidthVP(CONTEXT, PRELIM).p());
3699         for (AstCaseItem *nextip, *itemp = nodep->itemsp(); itemp; itemp = nextip) {
3700             nextip = VN_AS(itemp->nextp(), CaseItem);  // Prelim may cause the node to get replaced
3701             if (!VN_IS(nodep, GenCase)) userIterateAndNext(itemp->bodysp(), nullptr);
3702             for (AstNode *nextcp, *condp = itemp->condsp(); condp; condp = nextcp) {
3703                 nextcp = condp->nextp();  // Prelim may cause the node to get replaced
3704                 VL_DO_DANGLING(userIterate(condp, WidthVP(CONTEXT, PRELIM).p()), condp);
3705             }
3706         }
3707 
3708         // Take width as maximum across all items, if any is real whole thing is real
3709         AstNodeDType* subDTypep = nodep->exprp()->dtypep();
3710         for (AstCaseItem* itemp = nodep->itemsp(); itemp;
3711              itemp = VN_AS(itemp->nextp(), CaseItem)) {
3712             for (AstNode* condp = itemp->condsp(); condp; condp = condp->nextp()) {
3713                 if (condp->dtypep() != subDTypep) {
3714                     if (condp->dtypep()->isDouble()) {
3715                         subDTypep = nodep->findDoubleDType();
3716                     } else {
3717                         const int width = std::max(subDTypep->width(), condp->width());
3718                         const int mwidth = std::max(subDTypep->widthMin(), condp->widthMin());
3719                         const bool issigned = subDTypep->isSigned() && condp->isSigned();
3720                         subDTypep
3721                             = nodep->findLogicDType(width, mwidth, VSigning::fromBool(issigned));
3722                     }
3723                 }
3724             }
3725         }
3726         // Apply width
3727         iterateCheck(nodep, "Case expression", nodep->exprp(), CONTEXT, FINAL, subDTypep,
3728                      EXTEND_LHS);
3729         for (AstCaseItem* itemp = nodep->itemsp(); itemp;
3730              itemp = VN_AS(itemp->nextp(), CaseItem)) {
3731             for (AstNode *nextcp, *condp = itemp->condsp(); condp; condp = nextcp) {
3732                 nextcp = condp->nextp();  // Final may cause the node to get replaced
3733                 iterateCheck(nodep, "Case Item", condp, CONTEXT, FINAL, subDTypep, EXTEND_LHS);
3734             }
3735         }
3736     }
visit(AstNodeFor * nodep)3737     virtual void visit(AstNodeFor* nodep) override {
3738         assertAtStatement(nodep);
3739         userIterateAndNext(nodep->initsp(), nullptr);
3740         iterateCheckBool(nodep, "For Test Condition", nodep->condp(),
3741                          BOTH);  // it's like an if() condition.
3742         if (!VN_IS(nodep, GenFor)) userIterateAndNext(nodep->bodysp(), nullptr);
3743         userIterateAndNext(nodep->incsp(), nullptr);
3744     }
visit(AstRepeat * nodep)3745     virtual void visit(AstRepeat* nodep) override {
3746         assertAtStatement(nodep);
3747         userIterateAndNext(nodep->countp(), WidthVP(SELF, BOTH).p());
3748         userIterateAndNext(nodep->bodysp(), nullptr);
3749     }
visit(AstWhile * nodep)3750     virtual void visit(AstWhile* nodep) override {
3751         assertAtStatement(nodep);
3752         userIterateAndNext(nodep->precondsp(), nullptr);
3753         iterateCheckBool(nodep, "For Test Condition", nodep->condp(),
3754                          BOTH);  // it's like an if() condition.
3755         userIterateAndNext(nodep->bodysp(), nullptr);
3756         userIterateAndNext(nodep->incsp(), nullptr);
3757     }
visit(AstNodeIf * nodep)3758     virtual void visit(AstNodeIf* nodep) override {
3759         assertAtStatement(nodep);
3760         // if (debug()) nodep->dumpTree(cout, "  IfPre: ");
3761         if (!VN_IS(nodep, GenIf)) {  // for m_paramsOnly
3762             userIterateAndNext(nodep->ifsp(), nullptr);
3763             userIterateAndNext(nodep->elsesp(), nullptr);
3764         }
3765         iterateCheckBool(nodep, "If", nodep->condp(), BOTH);  // it's like an if() condition.
3766         // if (debug()) nodep->dumpTree(cout, "  IfOut: ");
3767     }
visit(AstExprStmt * nodep)3768     virtual void visit(AstExprStmt* nodep) override {
3769         userIterateAndNext(nodep->stmtsp(), nullptr);
3770         // expected result is same as parent's expected result
3771         userIterateAndNext(nodep->resultp(), m_vup);
3772         nodep->dtypeFrom(nodep->resultp());
3773     }
3774 
visit(AstNodeAssign * nodep)3775     virtual void visit(AstNodeAssign* nodep) override {
3776         // IEEE-2012 10.7, 11.8.2, 11.8.3, 11.5:  (Careful of 11.8.1 which is
3777         //                  only one step; final dtype depends on assign LHS.)
3778         //    Determine RHS type width and signing
3779         //    Propagate type down to *non-self-determined* operators
3780         //       Real propagates only across one operator if one side is real -
3781         //       handled in each visitor.
3782         //    Then LHS sign-extends only if *RHS* is signed
3783         assertAtStatement(nodep);
3784         // if (debug()) nodep->dumpTree(cout, "  AssignPre: ");
3785         {
3786             // if (debug()) nodep->dumpTree(cout, "-    assin:  ");
3787             userIterateAndNext(nodep->lhsp(), WidthVP(SELF, BOTH).p());
3788             UASSERT_OBJ(nodep->lhsp()->dtypep(), nodep, "How can LHS be untyped?");
3789             UASSERT_OBJ(nodep->lhsp()->dtypep()->widthSized(), nodep, "How can LHS be unsized?");
3790             nodep->dtypeFrom(nodep->lhsp());
3791             //
3792             // AstPattern needs to know the proposed data type of the lhs, so pass on the prelim
3793             userIterateAndNext(nodep->rhsp(), WidthVP(nodep->dtypep(), PRELIM).p());
3794             //
3795             // if (debug()) nodep->dumpTree(cout, "-    assign: ");
3796             AstNodeDType* const lhsDTypep
3797                 = nodep->lhsp()->dtypep();  // Note we use rhsp for context determined
3798             iterateCheckAssign(nodep, "Assign RHS", nodep->rhsp(), FINAL, lhsDTypep);
3799             // if (debug()) nodep->dumpTree(cout, "  AssignOut: ");
3800         }
3801         if (const AstBasicDType* const basicp = nodep->rhsp()->dtypep()->basicp()) {
3802             if (basicp->isEventValue()) {
3803                 // see t_event_copy.v for commentary on the mess involved
3804                 nodep->v3warn(E_UNSUPPORTED, "Unsupported: assignment of event data type");
3805             }
3806         }
3807         if (VN_IS(nodep->rhsp(), EmptyQueue)) {
3808             UINFO(9, "= {} -> .delete(): " << nodep);
3809             if (!VN_IS(nodep->lhsp()->dtypep()->skipRefp(), QueueDType)) {
3810                 nodep->v3warn(E_UNSUPPORTED,
3811                               "Unsupported/Illegal: empty queue ('{}') in this assign context");
3812                 VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
3813                 return;
3814             }
3815             AstMethodCall* const newp = new AstMethodCall{
3816                 nodep->fileline(), nodep->lhsp()->unlinkFrBack(), "delete", nullptr};
3817             newp->makeStatement();
3818             nodep->replaceWith(newp);
3819             VL_DO_DANGLING(pushDeletep(nodep), nodep);
3820             // Need to now convert it
3821             visit(newp);
3822             return;
3823         }
3824         if (const AstNewDynamic* const dynp = VN_CAST(nodep->rhsp(), NewDynamic)) {
3825             UINFO(9, "= new[] -> .resize(): " << nodep);
3826             AstCMethodHard* newp;
3827             if (!dynp->rhsp()) {
3828                 newp = new AstCMethodHard(nodep->fileline(), nodep->lhsp()->unlinkFrBack(),
3829                                           "renew", dynp->sizep()->unlinkFrBack());
3830             } else {
3831                 newp = new AstCMethodHard(nodep->fileline(), nodep->lhsp()->unlinkFrBack(),
3832                                           "renew_copy", dynp->sizep()->unlinkFrBack());
3833                 newp->addPinsp(dynp->rhsp()->unlinkFrBack());
3834             }
3835             newp->didWidth(true);
3836             newp->protect(false);
3837             newp->makeStatement();
3838             nodep->replaceWith(newp);
3839             VL_DO_DANGLING(pushDeletep(nodep), nodep);
3840             // return;
3841         }
3842     }
3843 
visit(AstSFormatF * nodep)3844     virtual void visit(AstSFormatF* nodep) override {
3845         // Excludes NodeDisplay, see below
3846         if (m_vup && !m_vup->prelim()) return;  // Can be called as statement or function
3847         // Just let all arguments seek their natural sizes
3848         userIterateChildren(nodep, WidthVP(SELF, BOTH).p());
3849         //
3850         UINFO(9, "  Display in " << nodep->text() << endl);
3851         string newFormat;
3852         bool inPct = false;
3853         AstNode* argp = nodep->exprsp();
3854         const string txt = nodep->text();
3855         string fmt;
3856         for (char ch : txt) {
3857             if (!inPct && ch == '%') {
3858                 inPct = true;
3859                 fmt = ch;
3860             } else if (inPct && (isdigit(ch) || ch == '.' || ch == '-')) {
3861                 fmt += ch;
3862             } else if (tolower(inPct)) {
3863                 inPct = false;
3864                 bool added = false;
3865                 switch (tolower(ch)) {
3866                 case '%': break;  // %% - just output a %
3867                 case 'm': break;  // %m - auto insert "name"
3868                 case 'l': break;  // %m - auto insert "library"
3869                 case 'd': {  // Convert decimal to either 'd' or '#'
3870                     if (argp) {
3871                         AstNode* const nextp = argp->nextp();
3872                         if (argp->isDouble()) {
3873                             spliceCvtS(argp, true, 64);
3874                             ch = '~';
3875                         } else if (argp->isSigned()) {  // Convert it
3876                             ch = '~';
3877                         }
3878                         argp = nextp;
3879                     }
3880                     break;
3881                 }
3882                 case 'p': {  // Pattern
3883                     const AstNodeDType* const dtypep = argp ? argp->dtypep()->skipRefp() : nullptr;
3884                     const AstBasicDType* const basicp = dtypep ? dtypep->basicp() : nullptr;
3885                     if (basicp && basicp->isString()) {
3886                         added = true;
3887                         newFormat += "\"%@\"";
3888                     } else if (basicp && basicp->isDouble()) {
3889                         added = true;
3890                         newFormat += "%g";
3891                     } else if (VN_IS(dtypep, AssocArrayDType)  //
3892                                || VN_IS(dtypep, ClassRefDType)  //
3893                                || VN_IS(dtypep, DynArrayDType)  //
3894                                || VN_IS(dtypep, QueueDType)) {
3895                         added = true;
3896                         newFormat += "%@";
3897                         AstNRelinker handle;
3898                         argp->unlinkFrBack(&handle);
3899                         AstCMath* const newp
3900                             = new AstCMath(nodep->fileline(), "VL_TO_STRING(", 0, true);
3901                         newp->addBodysp(argp);
3902                         newp->addBodysp(new AstText(nodep->fileline(), ")", true));
3903                         newp->dtypeSetString();
3904                         newp->pure(true);
3905                         newp->protect(false);
3906                         handle.relink(newp);
3907                     } else {
3908                         added = true;
3909                         if (fmt == "%0") {
3910                             newFormat += "'h%0h";  // IEEE our choice
3911                         } else {
3912                             newFormat += "%d";
3913                         }
3914                     }
3915                     if (argp) argp = argp->nextp();
3916                     break;
3917                 }
3918                 case 's': {  // Convert string to pack string
3919                     if (argp && argp->dtypep()->basicp()->isString()) {  // Convert it
3920                         ch = '@';
3921                     }
3922                     if (argp) argp = argp->nextp();
3923                     break;
3924                 }
3925                 case 't': {  // Convert decimal time to realtime
3926                     if (argp) {
3927                         AstNode* const nextp = argp->nextp();
3928                         if (argp->isDouble()) ch = '^';  // Convert it
3929                         if (nodep->timeunit().isNone()) {
3930                             nodep->v3fatalSrc("display %t has no time units");
3931                         }
3932                         argp = nextp;
3933                     }
3934                     break;
3935                 }
3936                 case 'f':  // FALLTHRU
3937                 case 'g': {
3938                     if (argp) {
3939                         AstNode* const nextp = argp->nextp();
3940                         if (!argp->isDouble()) {
3941                             iterateCheckReal(nodep, "Display argument", argp, BOTH);
3942                         }
3943                         argp = nextp;
3944                     }
3945                     break;
3946                 }
3947                 case '?': {  // Unspecified by user, guess
3948                     if (argp && argp->isDouble()) {
3949                         ch = 'g';
3950                     } else if (argp && argp->isString()) {
3951                         ch = '@';
3952                     } else if (nodep->missingArgChar() == 'd' && argp->isSigned()) {
3953                         ch = '~';
3954                     } else {
3955                         ch = nodep->missingArgChar();
3956                     }
3957                     if (argp) argp = argp->nextp();
3958                     break;
3959                 }
3960                 default: {  // Most operators, just move to next argument
3961                     if (argp) argp = argp->nextp();
3962                     break;
3963                 }
3964                 }  // switch
3965                 if (!added) {
3966                     fmt += ch;
3967                     newFormat += fmt;
3968                 }
3969             } else {
3970                 newFormat += ch;
3971             }
3972         }
3973         nodep->text(newFormat);
3974         UINFO(9, "  Display out " << nodep->text() << endl);
3975     }
visit(AstDisplay * nodep)3976     virtual void visit(AstDisplay* nodep) override {
3977         assertAtStatement(nodep);
3978         if (nodep->filep()) iterateCheckFileDesc(nodep, nodep->filep(), BOTH);
3979         // Just let all arguments seek their natural sizes
3980         userIterateChildren(nodep, WidthVP(SELF, BOTH).p());
3981     }
visit(AstElabDisplay * nodep)3982     virtual void visit(AstElabDisplay* nodep) override {
3983         assertAtStatement(nodep);
3984         // Just let all arguments seek their natural sizes
3985         userIterateChildren(nodep, WidthVP(SELF, BOTH).p());
3986         if (!m_paramsOnly) {
3987             V3Const::constifyParamsEdit(nodep->fmtp());  // fmtp may change
3988             string text = nodep->fmtp()->text();
3989             if (text.empty()) text = "Elaboration system task message (IEEE 1800-2017 20.11)";
3990             switch (nodep->displayType()) {
3991             case AstDisplayType::DT_INFO: nodep->v3warn(USERINFO, text); break;
3992             case AstDisplayType::DT_ERROR: nodep->v3warn(USERERROR, text); break;
3993             case AstDisplayType::DT_WARNING: nodep->v3warn(USERWARN, text); break;
3994             case AstDisplayType::DT_FATAL: nodep->v3warn(USERFATAL, text); break;
3995             default: UASSERT_OBJ(false, nodep, "Unexpected elaboration display type");
3996             }
3997             VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
3998         }
3999     }
visit(AstDumpCtl * nodep)4000     virtual void visit(AstDumpCtl* nodep) override {
4001         assertAtStatement(nodep);
4002         // Just let all arguments seek their natural sizes
4003         userIterateChildren(nodep, WidthVP(SELF, BOTH).p());
4004     }
visit(AstFOpen * nodep)4005     virtual void visit(AstFOpen* nodep) override {
4006         // Although a system function in IEEE, here a statement which sets the file pointer (MCD)
4007         assertAtStatement(nodep);
4008         iterateCheckFileDesc(nodep, nodep->filep(), BOTH);
4009         userIterateAndNext(nodep->filenamep(), WidthVP(SELF, BOTH).p());
4010         userIterateAndNext(nodep->modep(), WidthVP(SELF, BOTH).p());
4011     }
visit(AstFOpenMcd * nodep)4012     virtual void visit(AstFOpenMcd* nodep) override {
4013         assertAtStatement(nodep);
4014         iterateCheckFileDesc(nodep, nodep->filep(), BOTH);
4015         userIterateAndNext(nodep->filenamep(), WidthVP(SELF, BOTH).p());
4016     }
visit(AstFClose * nodep)4017     virtual void visit(AstFClose* nodep) override {
4018         assertAtStatement(nodep);
4019         iterateCheckFileDesc(nodep, nodep->filep(), BOTH);
4020     }
visit(AstFError * nodep)4021     virtual void visit(AstFError* nodep) override {
4022         if (m_vup->prelim()) {
4023             iterateCheckFileDesc(nodep, nodep->filep(), BOTH);
4024             // We only support string types, not packed array
4025             iterateCheckString(nodep, "$ferror string result", nodep->strp(), BOTH);
4026             nodep->dtypeSetLogicUnsized(32, 1, VSigning::SIGNED);  // Spec says integer return
4027         }
4028     }
visit(AstFEof * nodep)4029     virtual void visit(AstFEof* nodep) override {
4030         if (m_vup->prelim()) {
4031             iterateCheckFileDesc(nodep, nodep->filep(), BOTH);
4032             nodep->dtypeSetLogicUnsized(32, 1, VSigning::SIGNED);  // Spec says integer return
4033         }
4034     }
visit(AstFFlush * nodep)4035     virtual void visit(AstFFlush* nodep) override {
4036         assertAtStatement(nodep);
4037         if (nodep->filep()) iterateCheckFileDesc(nodep, nodep->filep(), BOTH);
4038     }
visit(AstFRewind * nodep)4039     virtual void visit(AstFRewind* nodep) override {
4040         iterateCheckFileDesc(nodep, nodep->filep(), BOTH);
4041         nodep->dtypeSetLogicUnsized(32, 1, VSigning::SIGNED);  // Spec says integer return
4042     }
visit(AstFTell * nodep)4043     virtual void visit(AstFTell* nodep) override {
4044         iterateCheckFileDesc(nodep, nodep->filep(), BOTH);
4045         nodep->dtypeSetLogicUnsized(32, 1, VSigning::SIGNED);  // Spec says integer return
4046     }
visit(AstFSeek * nodep)4047     virtual void visit(AstFSeek* nodep) override {
4048         iterateCheckFileDesc(nodep, nodep->filep(), BOTH);
4049         iterateCheckSigned32(nodep, "$fseek offset", nodep->offset(), BOTH);
4050         iterateCheckSigned32(nodep, "$fseek operation", nodep->operation(), BOTH);
4051         nodep->dtypeSetLogicUnsized(32, 1, VSigning::SIGNED);  // Spec says integer return
4052     }
visit(AstFGetC * nodep)4053     virtual void visit(AstFGetC* nodep) override {
4054         if (m_vup->prelim()) {
4055             iterateCheckFileDesc(nodep, nodep->filep(), BOTH);
4056             nodep->dtypeSetLogicUnsized(32, 8, VSigning::SIGNED);  // Spec says integer return
4057         }
4058     }
visit(AstFGetS * nodep)4059     virtual void visit(AstFGetS* nodep) override {
4060         if (m_vup->prelim()) {
4061             nodep->dtypeSetSigned32();  // Spec says integer return
4062             iterateCheckFileDesc(nodep, nodep->filep(), BOTH);
4063             userIterateAndNext(nodep->strgp(), WidthVP(SELF, BOTH).p());
4064         }
4065     }
visit(AstFUngetC * nodep)4066     virtual void visit(AstFUngetC* nodep) override {
4067         if (m_vup->prelim()) {
4068             iterateCheckFileDesc(nodep, nodep->filep(), BOTH);
4069             iterateCheckSigned32(nodep, "$fungetc character", nodep->charp(), BOTH);
4070             nodep->dtypeSetLogicUnsized(32, 8, VSigning::SIGNED);  // Spec says integer return
4071         }
4072     }
visit(AstFRead * nodep)4073     virtual void visit(AstFRead* nodep) override {
4074         if (m_vup->prelim()) {
4075             nodep->dtypeSetSigned32();  // Spec says integer return
4076             userIterateAndNext(nodep->memp(), WidthVP(SELF, BOTH).p());
4077             iterateCheckFileDesc(nodep, nodep->filep(), BOTH);
4078             if (nodep->startp()) {
4079                 iterateCheckSigned32(nodep, "$fread start", nodep->startp(), BOTH);
4080             }
4081             if (nodep->countp()) {
4082                 iterateCheckSigned32(nodep, "$fread count", nodep->countp(), BOTH);
4083             }
4084         }
4085     }
visit(AstFScanF * nodep)4086     virtual void visit(AstFScanF* nodep) override {
4087         if (m_vup->prelim()) {
4088             nodep->dtypeSetSigned32();  // Spec says integer return
4089             iterateCheckFileDesc(nodep, nodep->filep(), BOTH);
4090             userIterateAndNext(nodep->exprsp(), WidthVP(SELF, BOTH).p());
4091         }
4092     }
visit(AstSScanF * nodep)4093     virtual void visit(AstSScanF* nodep) override {
4094         if (m_vup->prelim()) {
4095             nodep->dtypeSetSigned32();  // Spec says integer return
4096             userIterateAndNext(nodep->fromp(), WidthVP(SELF, BOTH).p());
4097             userIterateAndNext(nodep->exprsp(), WidthVP(SELF, BOTH).p());
4098         }
4099     }
visit(AstSysIgnore * nodep)4100     virtual void visit(AstSysIgnore* nodep) override {
4101         userIterateAndNext(nodep->exprsp(), WidthVP(SELF, BOTH).p());
4102     }
visit(AstSystemF * nodep)4103     virtual void visit(AstSystemF* nodep) override {
4104         if (m_vup->prelim()) {
4105             userIterateAndNext(nodep->lhsp(), WidthVP(SELF, BOTH).p());
4106             nodep->dtypeSetSigned32();  // Spec says integer return
4107         }
4108     }
visit(AstSysFuncAsTask * nodep)4109     virtual void visit(AstSysFuncAsTask* nodep) override {
4110         assertAtStatement(nodep);
4111         userIterateAndNext(nodep->lhsp(), WidthVP(SELF, BOTH).p());
4112     }
visit(AstSystemT * nodep)4113     virtual void visit(AstSystemT* nodep) override {
4114         assertAtStatement(nodep);
4115         userIterateAndNext(nodep->lhsp(), WidthVP(SELF, BOTH).p());
4116     }
visit(AstNodeReadWriteMem * nodep)4117     virtual void visit(AstNodeReadWriteMem* nodep) override {
4118         assertAtStatement(nodep);
4119         userIterateAndNext(nodep->filenamep(), WidthVP(SELF, BOTH).p());
4120         userIterateAndNext(nodep->memp(), WidthVP(SELF, BOTH).p());
4121         const AstNodeDType* subp = nullptr;
4122         if (const AstAssocArrayDType* adtypep
4123             = VN_CAST(nodep->memp()->dtypep()->skipRefp(), AssocArrayDType)) {
4124             subp = adtypep->subDTypep();
4125             if (!adtypep->keyDTypep()->skipRefp()->basicp()
4126                 || !adtypep->keyDTypep()->skipRefp()->basicp()->keyword().isIntNumeric()) {
4127                 nodep->memp()->v3error(nodep->verilogKwd()
4128                                        << " address/key must be integral (IEEE 1800-2017 21.4.1)");
4129             }
4130         } else if (const AstUnpackArrayDType* const adtypep
4131                    = VN_CAST(nodep->memp()->dtypep()->skipRefp(), UnpackArrayDType)) {
4132             subp = adtypep->subDTypep();
4133         } else {
4134             nodep->memp()->v3warn(E_UNSUPPORTED,
4135                                   "Unsupported: "
4136                                       << nodep->verilogKwd()
4137                                       << " into other than unpacked or associative array");
4138         }
4139         if (subp
4140             && (!subp->skipRefp()->basicp()
4141                 || !subp->skipRefp()->basicp()->keyword().isIntNumeric())) {
4142             nodep->memp()->v3warn(E_UNSUPPORTED,
4143                                   "Unsupported: " << nodep->verilogKwd()
4144                                                   << " array values must be integral");
4145         }
4146         userIterateAndNext(nodep->lsbp(), WidthVP(SELF, BOTH).p());
4147         userIterateAndNext(nodep->msbp(), WidthVP(SELF, BOTH).p());
4148     }
visit(AstValuePlusArgs * nodep)4149     virtual void visit(AstValuePlusArgs* nodep) override {
4150         if (m_vup->prelim()) {
4151             userIterateAndNext(nodep->searchp(), WidthVP(SELF, BOTH).p());
4152             userIterateAndNext(nodep->outp(), WidthVP(SELF, BOTH).p());
4153             nodep->dtypeChgWidthSigned(32, 1, VSigning::SIGNED);  // Spec says integer return
4154         }
4155     }
visit(AstTimeFormat * nodep)4156     virtual void visit(AstTimeFormat* nodep) override {
4157         assertAtStatement(nodep);
4158         iterateCheckSigned32(nodep, "units", nodep->unitsp(), BOTH);
4159         iterateCheckSigned32(nodep, "precision", nodep->precisionp(), BOTH);
4160         iterateCheckString(nodep, "suffix", nodep->suffixp(), BOTH);
4161         iterateCheckSigned32(nodep, "width", nodep->widthp(), BOTH);
4162     }
visit(AstUCStmt * nodep)4163     virtual void visit(AstUCStmt* nodep) override {
4164         // Just let all arguments seek their natural sizes
4165         assertAtStatement(nodep);
4166         userIterateChildren(nodep, WidthVP(SELF, BOTH).p());
4167     }
visit(AstAssert * nodep)4168     virtual void visit(AstAssert* nodep) override {
4169         assertAtStatement(nodep);
4170         iterateCheckBool(nodep, "Property", nodep->propp(), BOTH);  // it's like an if() condition.
4171         userIterateAndNext(nodep->passsp(), nullptr);
4172         userIterateAndNext(nodep->failsp(), nullptr);
4173     }
visit(AstAssertIntrinsic * nodep)4174     virtual void visit(AstAssertIntrinsic* nodep) override {
4175         assertAtStatement(nodep);
4176         iterateCheckBool(nodep, "Property", nodep->propp(), BOTH);  // it's like an if() condition.
4177         userIterateAndNext(nodep->passsp(), nullptr);
4178         userIterateAndNext(nodep->failsp(), nullptr);
4179     }
visit(AstCover * nodep)4180     virtual void visit(AstCover* nodep) override {
4181         assertAtStatement(nodep);
4182         iterateCheckBool(nodep, "Property", nodep->propp(), BOTH);  // it's like an if() condition.
4183         userIterateAndNext(nodep->passsp(), nullptr);
4184     }
visit(AstRestrict * nodep)4185     virtual void visit(AstRestrict* nodep) override {
4186         assertAtStatement(nodep);
4187         iterateCheckBool(nodep, "Property", nodep->propp(), BOTH);  // it's like an if() condition.
4188     }
visit(AstPin * nodep)4189     virtual void visit(AstPin* nodep) override {
4190         // if (debug()) nodep->dumpTree(cout, "-  PinPre: ");
4191         // TOP LEVEL NODE
4192         if (nodep->modVarp() && nodep->modVarp()->isGParam()) {
4193             // Widthing handled as special init() case
4194             if (auto* const patternp = VN_CAST(nodep->exprp(), Pattern))
4195                 if (const auto* modVarp = nodep->modVarp())
4196                     patternp->childDTypep(modVarp->childDTypep()->cloneTree(false));
4197             userIterateChildren(nodep, WidthVP(SELF, BOTH).p());
4198         } else if (!m_paramsOnly) {
4199             if (!nodep->modVarp()->didWidth()) {
4200                 // Var hasn't been widthed, so make it so.
4201                 userIterate(nodep->modVarp(), nullptr);
4202             }
4203             if (!nodep->exprp()) {  // No-connect
4204                 return;
4205             }
4206             // Very much like like an assignment, but which side is LH/RHS
4207             // depends on pin being a in/output/inout.
4208             userIterateAndNext(nodep->exprp(), WidthVP(nodep->modVarp()->dtypep(), PRELIM).p());
4209             AstNodeDType* modDTypep = nodep->modVarp()->dtypep();
4210             AstNodeDType* conDTypep = nodep->exprp()->dtypep();
4211             if (!modDTypep) nodep->v3fatalSrc("Unlinked pin data type");
4212             if (!conDTypep) nodep->v3fatalSrc("Unlinked pin data type");
4213             modDTypep = modDTypep->skipRefp();
4214             conDTypep = conDTypep->skipRefp();
4215             AstNodeDType* subDTypep = modDTypep;
4216             const int pinwidth = modDTypep->width();
4217             const int conwidth = conDTypep->width();
4218             if (conDTypep == modDTypep  // If match, we're golden
4219                 || similarDTypeRecurse(conDTypep, modDTypep)) {
4220                 userIterateAndNext(nodep->exprp(), WidthVP(subDTypep, FINAL).p());
4221             } else if (m_cellp->rangep()) {
4222                 const int numInsts = m_cellp->rangep()->elementsConst();
4223                 if (conwidth == pinwidth) {
4224                     // Arrayed instants: widths match so connect to each instance
4225                     subDTypep = conDTypep;  // = same expr dtype
4226                 } else if (conwidth == numInsts * pinwidth) {
4227                     // Arrayed instants: one bit for each of the instants (each
4228                     // assign is 1 pinwidth wide)
4229                     subDTypep = conDTypep;  // = same expr dtype (but numInst*pin_dtype)
4230                 } else {
4231                     // Must be a error according to spec
4232                     // (Because we need to know if to connect to one or all instants)
4233                     nodep->v3error(ucfirst(nodep->prettyOperatorName())
4234                                    << " as part of a module instance array"
4235                                    << " requires " << pinwidth << " or " << pinwidth * numInsts
4236                                    << " bits, but connection's "
4237                                    << nodep->exprp()->prettyTypeName() << " generates " << conwidth
4238                                    << " bits. (IEEE 1800-2017 23.3.3)");
4239                     subDTypep = conDTypep;  // = same expr dtype
4240                 }
4241                 userIterateAndNext(nodep->exprp(), WidthVP(subDTypep, FINAL).p());
4242             } else {
4243                 if (nodep->modVarp()->direction() == VDirection::REF) {
4244                     nodep->v3error("Ref connection "
4245                                    << nodep->modVarp()->prettyNameQ()
4246                                    << " requires matching types;"
4247                                    << " ref requires " << modDTypep->prettyDTypeNameQ()
4248                                    << " data type but connection is "
4249                                    << conDTypep->prettyDTypeNameQ() << " data type.");
4250                 } else if (nodep->modVarp()->isTristate()) {
4251                     if (pinwidth != conwidth) {
4252                         // Ideally should call pinReconnectSimple which would tolerate this
4253                         // then have a conversion warning
4254                         nodep->v3warn(E_UNSUPPORTED,
4255                                       "Unsupported: " << ucfirst(nodep->prettyOperatorName())
4256                                                       << " to inout signal requires " << pinwidth
4257                                                       << " bits, but connection's "
4258                                                       << nodep->exprp()->prettyTypeName()
4259                                                       << " generates " << conwidth << " bits.");
4260                         // otherwise would need some mess to force both sides to proper size
4261                     }
4262                 } else if (nodep->modVarp()->direction().isWritable()
4263                            && ((conDTypep->isDouble() && !modDTypep->isDouble())
4264                                || (!conDTypep->isDouble() && modDTypep->isDouble()))) {
4265                     nodep->v3warn(E_UNSUPPORTED,
4266                                   "Unsupported: " << ucfirst(nodep->prettyOperatorName())
4267                                                   << " connects real to non-real");
4268                 }
4269 
4270                 // Check if an interface is connected to a non-interface and vice versa
4271                 if ((VN_IS(modDTypep, IfaceRefDType) && !VN_IS(conDTypep, IfaceRefDType))
4272                     || (VN_IS(conDTypep, IfaceRefDType) && !VN_IS(modDTypep, IfaceRefDType))) {
4273                     nodep->v3error("Illegal " << nodep->prettyOperatorName() << ","
4274                                               << " mismatch between port which is"
4275                                               << (VN_CAST(modDTypep, IfaceRefDType) ? "" : " not")
4276                                               << " an interface,"
4277                                               << " and expression which is"
4278                                               << (VN_CAST(conDTypep, IfaceRefDType) ? "" : " not")
4279                                               << " an interface.");
4280                 }
4281 
4282                 // TODO Simple dtype checking, should be a more general check
4283                 const AstNodeArrayDType* const exprArrayp = VN_CAST(conDTypep, UnpackArrayDType);
4284                 const AstNodeArrayDType* const modArrayp = VN_CAST(modDTypep, UnpackArrayDType);
4285                 if (exprArrayp && modArrayp && VN_IS(exprArrayp->subDTypep(), IfaceRefDType)
4286                     && exprArrayp->declRange().elements() != modArrayp->declRange().elements()) {
4287                     const int exprSize = exprArrayp->declRange().elements();
4288                     const int modSize = modArrayp->declRange().elements();
4289                     nodep->v3error("Illegal "
4290                                    << nodep->prettyOperatorName() << ","
4291                                    << " mismatch between port which is an interface array of size "
4292                                    << modSize << ","
4293                                    << " and expression which is an interface array of size "
4294                                    << exprSize << ".");
4295                     UINFO(1, "    Related lo: " << modDTypep << endl);
4296                     UINFO(1, "    Related hi: " << conDTypep << endl);
4297                 } else if ((exprArrayp && !modArrayp) || (!exprArrayp && modArrayp)) {
4298                     nodep->v3error("Illegal " << nodep->prettyOperatorName() << ","
4299                                               << " mismatch between port which is"
4300                                               << (modArrayp ? "" : " not") << " an array,"
4301                                               << " and expression which is"
4302                                               << (exprArrayp ? "" : " not")
4303                                               << " an array. (IEEE 1800-2017 7.6)");
4304                     UINFO(1, "    Related lo: " << modDTypep << endl);
4305                     UINFO(1, "    Related hi: " << conDTypep << endl);
4306                 }
4307                 iterateCheckAssign(nodep, "pin connection", nodep->exprp(), FINAL, subDTypep);
4308             }
4309         }
4310         // if (debug()) nodep->dumpTree(cout, "-  PinOut: ");
4311     }
visit(AstCell * nodep)4312     virtual void visit(AstCell* nodep) override {
4313         VL_RESTORER(m_cellp);
4314         m_cellp = nodep;
4315         if (!m_paramsOnly) {
4316             if (VN_IS(nodep->modp(), NotFoundModule)) {
4317                 // We've resolved parameters and hit a module that we couldn't resolve.  It's
4318                 // finally time to report it.
4319                 // Note only here in V3Width as this is first visitor after V3Dead.
4320                 nodep->modNameFileline()->v3error("Cannot find file containing module: '"
4321                                                   << nodep->modName() << "'");
4322                 v3Global.opt.filePathLookedMsg(nodep->modNameFileline(), nodep->modName());
4323             }
4324             if (nodep->rangep()) userIterateAndNext(nodep->rangep(), WidthVP(SELF, BOTH).p());
4325             userIterateAndNext(nodep->pinsp(), nullptr);
4326         }
4327         userIterateAndNext(nodep->paramsp(), nullptr);
4328     }
visit(AstGatePin * nodep)4329     virtual void visit(AstGatePin* nodep) override {
4330         if (m_vup->prelim()) {
4331             userIterateAndNext(nodep->rangep(), WidthVP(SELF, BOTH).p());
4332             userIterateAndNext(nodep->exprp(), WidthVP(CONTEXT, PRELIM).p());
4333             nodep->dtypeFrom(nodep->rangep());
4334             // Very much like like an pin
4335             const AstNodeDType* const conDTypep = nodep->exprp()->dtypep();
4336             const int numInsts = nodep->rangep()->elementsConst();
4337             const int pinwidth = numInsts;
4338             const int conwidth = conDTypep->width();
4339             if (conwidth == 1 && pinwidth > 1) {  // Multiple connections
4340                 AstNodeDType* const subDTypep = nodep->findLogicDType(1, 1, conDTypep->numeric());
4341                 userIterateAndNext(nodep->exprp(), WidthVP(subDTypep, FINAL).p());
4342                 AstNode* const newp = new AstReplicate(nodep->fileline(),
4343                                                        nodep->exprp()->unlinkFrBack(), numInsts);
4344                 nodep->replaceWith(newp);
4345             } else {
4346                 // Eliminating so pass down all of vup
4347                 userIterateAndNext(nodep->exprp(), m_vup);
4348                 nodep->replaceWith(nodep->exprp()->unlinkFrBack());
4349             }
4350             VL_DO_DANGLING(pushDeletep(nodep), nodep);
4351         }
4352     }
visit(AstNodeFTask * nodep)4353     virtual void visit(AstNodeFTask* nodep) override {
4354         // Grab width from the output variable (if it's a function)
4355         if (nodep->didWidth()) return;
4356         if (nodep->doingWidth()) {
4357             nodep->v3warn(E_UNSUPPORTED, "Unsupported: Recursive function or task call");
4358             nodep->dtypeSetBit();
4359             nodep->didWidth(true);
4360             return;
4361         }
4362         if (nodep->classMethod() && nodep->name() == "rand_mode") {
4363             nodep->v3error("The 'rand_mode' method is built-in and cannot be overridden"
4364                            " (IEEE 1800-2017 18.8)");
4365         } else if (nodep->classMethod() && nodep->name() == "constraint_mode") {
4366             nodep->v3error("The 'constraint_mode' method is built-in and cannot be overridden"
4367                            " (IEEE 1800-2017 18.9)");
4368         }
4369         // Function hasn't been widthed, so make it so.
4370         // Would use user1 etc, but V3Width called from too many places to spend a user
4371         nodep->doingWidth(true);
4372         m_ftaskp = nodep;
4373         userIterateChildren(nodep, nullptr);
4374         if (nodep->isConstructor()) {
4375             // Pretend it's void so less special casing needed when look at dtypes
4376             nodep->dtypeSetVoid();
4377         } else if (nodep->fvarp()) {
4378             m_funcp = VN_AS(nodep, Func);
4379             UASSERT_OBJ(m_funcp, nodep, "FTask with function variable, but isn't a function");
4380             nodep->dtypeFrom(nodep->fvarp());  // Which will get it from fvarp()->dtypep()
4381         }
4382         nodep->didWidth(true);
4383         nodep->doingWidth(false);
4384         m_funcp = nullptr;
4385         m_ftaskp = nullptr;
4386         if (nodep->dpiImport() && !nodep->dpiOpenParent() && markHasOpenArray(nodep)) {
4387             nodep->dpiOpenParentInc();  // Mark so V3Task will wait for a child to build calling
4388                                         // func
4389         }
4390     }
visit(AstReturn * nodep)4391     virtual void visit(AstReturn* nodep) override {
4392         // IEEE: Assignment-like context
4393         assertAtStatement(nodep);
4394         if (!m_funcp) {
4395             if (nodep->lhsp()) {  // Return w/o value ok other places
4396                 nodep->v3error("Return with return value isn't underneath a function");
4397             }
4398         } else {
4399             if (nodep->lhsp()) {
4400                 // Function hasn't been widthed, so make it so.
4401                 nodep->dtypeFrom(m_funcp->fvarp());
4402                 // AstPattern requires assignments to pass datatype on PRELIM
4403                 userIterateAndNext(nodep->lhsp(), WidthVP(nodep->dtypep(), PRELIM).p());
4404                 iterateCheckAssign(nodep, "Return value", nodep->lhsp(), FINAL, nodep->dtypep());
4405             }
4406         }
4407     }
4408 
visit(AstFuncRef * nodep)4409     virtual void visit(AstFuncRef* nodep) override {
4410         visit(static_cast<AstNodeFTaskRef*>(nodep));
4411         nodep->dtypeFrom(nodep->taskp());
4412         // if (debug()) nodep->dumpTree(cout, "  FuncOut: ");
4413     }
4414     // Returns true if dtypep0 and dtypep1 have same dimensions
areSameSize(AstUnpackArrayDType * dtypep0,AstUnpackArrayDType * dtypep1)4415     static bool areSameSize(AstUnpackArrayDType* dtypep0, AstUnpackArrayDType* dtypep1) {
4416         const std::vector<AstUnpackArrayDType*> dims0 = dtypep0->unpackDimensions();
4417         const std::vector<AstUnpackArrayDType*> dims1 = dtypep1->unpackDimensions();
4418         if (dims0.size() != dims1.size()) return false;
4419         for (size_t i = 0; i < dims0.size(); ++i) {
4420             if (dims0[i]->elementsConst() != dims1[i]->elementsConst()) return false;
4421         }
4422         return true;
4423     }
4424     // Makes sure that port and pin have same size and same datatype
checkUnpackedArrayArgs(AstVar * portp,AstNode * pinp)4425     void checkUnpackedArrayArgs(AstVar* portp, AstNode* pinp) {
4426         if (AstUnpackArrayDType* const portDtypep
4427             = VN_CAST(portp->dtypep()->skipRefp(), UnpackArrayDType)) {
4428             if (AstUnpackArrayDType* const pinDtypep
4429                 = VN_CAST(pinp->dtypep()->skipRefp(), UnpackArrayDType)) {
4430                 if (!areSameSize(portDtypep, pinDtypep)) {
4431                     pinp->v3warn(E_UNSUPPORTED,
4432                                  "Shape of the argument does not match the shape of the parameter "
4433                                      << "(" << pinDtypep->prettyDTypeNameQ() << " v.s. "
4434                                      << portDtypep->prettyDTypeNameQ() << ")");
4435                 }
4436                 if (portDtypep->basicp()->width() != pinDtypep->basicp()->width()
4437                     || (portDtypep->basicp()->keyword() != pinDtypep->basicp()->keyword()
4438                         && !(portDtypep->basicp()->keyword() == AstBasicDTypeKwd::LOGIC_IMPLICIT
4439                              && pinDtypep->basicp()->keyword() == AstBasicDTypeKwd::LOGIC)
4440                         && !(portDtypep->basicp()->keyword() == AstBasicDTypeKwd::LOGIC
4441                              && pinDtypep->basicp()->keyword()
4442                                     == AstBasicDTypeKwd::LOGIC_IMPLICIT))) {
4443                     pinp->v3warn(E_UNSUPPORTED,
4444                                  "Shape of the argument does not match the shape of the parameter "
4445                                      << "(" << pinDtypep->basicp()->prettyDTypeNameQ() << " v.s. "
4446                                      << portDtypep->basicp()->prettyDTypeNameQ() << ")");
4447                 }
4448             } else {
4449                 pinp->v3warn(E_UNSUPPORTED, "Argument is not an unpacked array while parameter "
4450                                                 << portp->prettyNameQ() << " is");
4451             }
4452         }
4453     }
processFTaskRefArgs(AstNodeFTaskRef * nodep)4454     void processFTaskRefArgs(AstNodeFTaskRef* nodep) {
4455         // For arguments, is assignment-like context; see IEEE rules in AstNodeAssign
4456         // Function hasn't been widthed, so make it so.
4457         UINFO(5, "  FTASKREF " << nodep << endl);
4458         UASSERT_OBJ(nodep->taskp(), nodep, "Unlinked");
4459         if (nodep->didWidth()) return;
4460         userIterate(nodep->taskp(), nullptr);
4461         //
4462         // And do the arguments to the task/function too
4463         do {
4464         reloop:
4465             const V3TaskConnects tconnects = V3Task::taskConnects(nodep, nodep->taskp()->stmtsp());
4466             for (const auto& tconnect : tconnects) {
4467                 const AstVar* const portp = tconnect.first;
4468                 AstArg* const argp = tconnect.second;
4469                 AstNode* pinp = argp->exprp();
4470                 if (!pinp) continue;  // Argument error we'll find later
4471                 // Prelim may cause the node to get replaced; we've lost our
4472                 // pointer, so need to iterate separately later
4473                 if (portp->attrSFormat()
4474                     && (!VN_IS(pinp, SFormatF) || pinp->nextp())) {  // Not already done
4475                     UINFO(4, "   sformat via metacomment: " << nodep << endl);
4476                     AstNRelinker handle;
4477                     argp->unlinkFrBackWithNext(&handle);  // Format + additional args, if any
4478                     AstNode* argsp = nullptr;
4479                     while (AstArg* const nextargp = VN_AS(argp->nextp(), Arg)) {
4480                         argsp = AstNode::addNext(
4481                             argsp, nextargp->exprp()
4482                                        ->unlinkFrBackWithNext());  // Expression goes to SFormatF
4483                         nextargp->unlinkFrBack()->deleteTree();  // Remove the call's Arg wrapper
4484                     }
4485                     string format;
4486                     if (VN_IS(pinp, Const)) {
4487                         format = VN_AS(pinp, Const)->num().toString();
4488                     } else {
4489                         pinp->v3error(
4490                             "Format to $display-like function must have constant format string");
4491                     }
4492                     VL_DO_DANGLING(pushDeletep(argp), argp);
4493                     AstSFormatF* const newp
4494                         = new AstSFormatF(nodep->fileline(), format, false, argsp);
4495                     if (!newp->scopeNamep() && newp->formatScopeTracking()) {
4496                         newp->scopeNamep(new AstScopeName{newp->fileline(), true});
4497                     }
4498                     handle.relink(new AstArg(newp->fileline(), "", newp));
4499                     // Connection list is now incorrect (has extra args in it).
4500                     goto reloop;  // so exit early; next loop will correct it
4501                 }  //
4502                 else if (portp->basicp() && portp->basicp()->keyword() == AstBasicDTypeKwd::STRING
4503                          && !VN_IS(pinp, CvtPackString)
4504                          && !VN_IS(pinp, SFormatF)  // Already generates a string
4505                          && !VN_IS(portp->dtypep(), UnpackArrayDType)  // Unpacked array must match
4506                          && !(VN_IS(pinp, VarRef)
4507                               && VN_AS(pinp, VarRef)->varp()->basicp()->keyword()
4508                                      == AstBasicDTypeKwd::STRING)) {
4509                     UINFO(4, "   Add CvtPackString: " << pinp << endl);
4510                     AstNRelinker handle;
4511                     pinp->unlinkFrBack(&handle);  // No next, that's the next pin
4512                     AstNode* const newp = new AstCvtPackString(pinp->fileline(), pinp);
4513                     handle.relink(newp);
4514                     pinp = newp;
4515                 }
4516                 // AstPattern requires assignments to pass datatype on PRELIM
4517                 VL_DO_DANGLING(userIterate(pinp, WidthVP(portp->dtypep(), PRELIM).p()), pinp);
4518             }
4519         } while (false);
4520         // Stage 2
4521         {
4522             const V3TaskConnects tconnects = V3Task::taskConnects(nodep, nodep->taskp()->stmtsp());
4523             for (const auto& tconnect : tconnects) {
4524                 AstVar* const portp = tconnect.first;
4525                 const AstArg* const argp = tconnect.second;
4526                 AstNode* const pinp = argp->exprp();
4527                 if (!pinp) continue;  // Argument error we'll find later
4528                 // Change data types based on above accept completion
4529                 if (nodep->taskp()->dpiImport()) checkUnpackedArrayArgs(portp, pinp);
4530                 if (portp->isDouble()) VL_DO_DANGLING(spliceCvtD(pinp), pinp);
4531             }
4532         }
4533         // Stage 3
4534         {
4535             const V3TaskConnects tconnects = V3Task::taskConnects(nodep, nodep->taskp()->stmtsp());
4536             for (const auto& tconnect : tconnects) {
4537                 const AstVar* const portp = tconnect.first;
4538                 const AstArg* const argp = tconnect.second;
4539                 AstNode* const pinp = argp->exprp();
4540                 if (!pinp) continue;  // Argument error we'll find later
4541                 // Do PRELIM again, because above accept may have exited early
4542                 // due to node replacement
4543                 userIterate(pinp, WidthVP(portp->dtypep(), PRELIM).p());
4544             }
4545         }
4546         // Cleanup any open arrays
4547         if (markHasOpenArray(nodep->taskp())) makeOpenArrayShell(nodep);
4548         // Stage 4
4549         {
4550             const V3TaskConnects tconnects = V3Task::taskConnects(nodep, nodep->taskp()->stmtsp());
4551             for (const auto& tconnect : tconnects) {
4552                 const AstVar* const portp = tconnect.first;
4553                 const AstArg* const argp = tconnect.second;
4554                 AstNode* const pinp = argp->exprp();
4555                 if (!pinp) continue;  // Argument error we'll find later
4556                 if (portp->direction() == VDirection::REF
4557                     && !similarDTypeRecurse(portp->dtypep(), pinp->dtypep())) {
4558                     pinp->v3error("Ref argument requires matching types;"
4559                                   << " port " << portp->prettyNameQ() << " requires "
4560                                   << portp->prettyTypeName() << " but connection is "
4561                                   << pinp->prettyTypeName() << ".");
4562                 } else if (portp->isWritable() && pinp->width() != portp->width()) {
4563                     pinp->v3warn(E_UNSUPPORTED, "Unsupported: Function output argument "
4564                                                     << portp->prettyNameQ() << " requires "
4565                                                     << portp->width() << " bits, but connection's "
4566                                                     << pinp->prettyTypeName() << " generates "
4567                                                     << pinp->width() << " bits.");
4568                     // otherwise would need some mess to force both sides to proper size
4569                     // (get an ASSIGN with EXTEND on the lhs instead of rhs)
4570                 }
4571                 if (!portp->basicp() || portp->basicp()->isOpaque()) {
4572                     userIterate(pinp, WidthVP(portp->dtypep(), FINAL).p());
4573                 } else {
4574                     iterateCheckAssign(nodep, "Function Argument", pinp, FINAL, portp->dtypep());
4575                 }
4576             }
4577         }
4578     }
visit(AstNodeFTaskRef * nodep)4579     virtual void visit(AstNodeFTaskRef* nodep) override {
4580         // For arguments, is assignment-like context; see IEEE rules in AstNodeAssign
4581         // Function hasn't been widthed, so make it so.
4582         UINFO(5, "  FTASKREF " << nodep << endl);
4583         UASSERT_OBJ(nodep->taskp(), nodep, "Unlinked");
4584         if (nodep->didWidth()) return;
4585         userIterate(nodep->taskp(), nullptr);
4586         // And do the arguments to the task/function too
4587         processFTaskRefArgs(nodep);
4588         nodep->didWidth(true);
4589     }
visit(AstNodeProcedure * nodep)4590     virtual void visit(AstNodeProcedure* nodep) override {
4591         assertAtStatement(nodep);
4592         m_procedurep = nodep;
4593         userIterateChildren(nodep, nullptr);
4594         m_procedurep = nullptr;
4595     }
visit(AstWith * nodep)4596     virtual void visit(AstWith* nodep) override {
4597         // Should otherwise be underneath a method call
4598         AstNodeDType* const vdtypep = m_vup->dtypeNullSkipRefp();
4599         {
4600             VL_RESTORER(m_withp);
4601             m_withp = nodep;
4602             userIterateChildren(nodep->indexArgRefp(), nullptr);
4603             userIterateChildren(nodep->valueArgRefp(), nullptr);
4604             if (vdtypep) {
4605                 userIterateAndNext(nodep->exprp(), WidthVP(nodep->dtypep(), PRELIM).p());
4606             } else {  // 'sort with' allows arbitrary type
4607                 userIterateAndNext(nodep->exprp(), WidthVP(SELF, PRELIM).p());
4608             }
4609             nodep->dtypeFrom(nodep->exprp());
4610             iterateCheckAssign(nodep, "'with' return value", nodep->exprp(), FINAL,
4611                                nodep->dtypep());
4612         }
4613     }
visit(AstLambdaArgRef * nodep)4614     virtual void visit(AstLambdaArgRef* nodep) override {
4615         UASSERT_OBJ(m_withp, nodep, "LambdaArgRef not underneath 'with' lambda");
4616         if (nodep->index()) {
4617             nodep->dtypeFrom(m_withp->indexArgRefp());
4618         } else {
4619             nodep->dtypeFrom(m_withp->valueArgRefp());
4620         }
4621     }
visit(AstNetlist * nodep)4622     virtual void visit(AstNetlist* nodep) override {
4623         // Iterate modules backwards, in bottom-up order.  That's faster
4624         userIterateChildrenBackwards(nodep, nullptr);
4625     }
4626 
4627     //--------------------
4628     // Default
visit(AstNodeMath * nodep)4629     virtual void visit(AstNodeMath* nodep) override {
4630         if (!nodep->didWidth()) {
4631             nodep->v3fatalSrc(
4632                 "Visit function missing? Widthed function missing for math node: " << nodep);
4633         }
4634         userIterateChildren(nodep, nullptr);
4635     }
visit(AstNode * nodep)4636     virtual void visit(AstNode* nodep) override {
4637         // Default: Just iterate
4638         UASSERT_OBJ(!m_vup, nodep,
4639                     "Visit function missing? Widthed expectation for this node: " << nodep);
4640         userIterateChildren(nodep, nullptr);
4641     }
4642 
4643     //----------------------------------------------------------------------
4644     // WIDTH METHODs -- all iterate
4645 
visit_Or_Lu64(AstNodeUniop * nodep)4646     void visit_Or_Lu64(AstNodeUniop* nodep) {
4647         // CALLER: AstBitsToRealD
4648         // Real: Output real
4649         // LHS presumed self-determined, then coerced to real
4650         if (m_vup->prelim()) {  // First stage evaluation
4651             nodep->dtypeSetDouble();
4652             AstNodeDType* const subDTypep = nodep->findLogicDType(64, 64, VSigning::UNSIGNED);
4653             // Self-determined operand
4654             userIterateAndNext(nodep->lhsp(), WidthVP(SELF, PRELIM).p());
4655             iterateCheck(nodep, "LHS", nodep->lhsp(), SELF, FINAL, subDTypep, EXTEND_EXP);
4656         }
4657     }
visit(AstIToRD * nodep)4658     virtual void visit(AstIToRD* nodep) override {
4659         // Real: Output real
4660         // LHS presumed self-determined, then coerced to real
4661         if (m_vup->prelim()) {  // First stage evaluation
4662             nodep->dtypeSetDouble();
4663             // Self-determined operand (TODO check if numeric type)
4664             userIterateAndNext(nodep->lhsp(), WidthVP(SELF, PRELIM).p());
4665             if (nodep->lhsp()->isSigned()) {
4666                 nodep->replaceWith(
4667                     new AstISToRD(nodep->fileline(), nodep->lhsp()->unlinkFrBack()));
4668                 VL_DO_DANGLING(nodep->deleteTree(), nodep);
4669             }
4670         }
4671     }
visit(AstISToRD * nodep)4672     virtual void visit(AstISToRD* nodep) override {
4673         // Real: Output real
4674         // LHS presumed self-determined, then coerced to real
4675         if (m_vup->prelim()) {  // First stage evaluation
4676             nodep->dtypeSetDouble();
4677             // Self-determined operand (TODO check if numeric type)
4678             userIterateAndNext(nodep->lhsp(), WidthVP(SELF, PRELIM).p());
4679         }
4680     }
visit_Os32_Lr(AstNodeUniop * nodep)4681     void visit_Os32_Lr(AstNodeUniop* nodep) {
4682         // CALLER: RToI
4683         // Real: LHS real
4684         // LHS presumed self-determined, then coerced to real
4685         if (m_vup->prelim()) {  // First stage evaluation
4686             iterateCheckReal(nodep, "LHS", nodep->lhsp(), BOTH);
4687             nodep->dtypeSetSigned32();
4688         }
4689     }
visit_Ou64_Lr(AstNodeUniop * nodep)4690     void visit_Ou64_Lr(AstNodeUniop* nodep) {
4691         // CALLER: RealToBits
4692         // Real: LHS real
4693         // LHS presumed self-determined, then coerced to real
4694         if (m_vup->prelim()) {  // First stage evaluation
4695             iterateCheckReal(nodep, "LHS", nodep->lhsp(), BOTH);
4696             nodep->dtypeSetUInt64();
4697         }
4698     }
4699 
visit_log_not(AstNode * nodep)4700     void visit_log_not(AstNode* nodep) {
4701         // CALLER: LogNot
4702         // Width-check: lhs 1 bit
4703         // Real: Allowed; implicitly compares with zero
4704         // We calculate the width of the UNDER expression.
4705         // We then check its width to see if it's legal, and edit if not
4706         // We finally set the width of our output
4707         // IEEE-2012: Table 11-21 and 11.8.1 (same as RedAnd):
4708         //   LHS is self-determined
4709         //   Width: 1 bit out
4710         //   Sign: unsigned out (11.8.1)
4711         UASSERT_OBJ(!nodep->op2p(), nodep, "For unary ops only!");
4712         if (m_vup->prelim()) {
4713             iterateCheckBool(nodep, "LHS", nodep->op1p(), BOTH);
4714             nodep->dtypeSetBit();
4715         }
4716     }
visit_log_and_or(AstNodeBiop * nodep)4717     void visit_log_and_or(AstNodeBiop* nodep) {
4718         // CALLER: LogAnd, LogOr, LogEq, LogIf
4719         // Widths: 1 bit out, lhs 1 bit, rhs 1 bit
4720         // IEEE-2012 Table 11-21:
4721         //   LHS is self-determined
4722         //   RHS is self-determined
4723         if (m_vup->prelim()) {
4724             iterateCheckBool(nodep, "LHS", nodep->lhsp(), BOTH);
4725             iterateCheckBool(nodep, "RHS", nodep->rhsp(), BOTH);
4726             nodep->dtypeSetBit();
4727         }
4728     }
visit_red_and_or(AstNodeUniop * nodep)4729     void visit_red_and_or(AstNodeUniop* nodep) {
4730         // CALLER: RedAnd, RedOr, ...
4731         // Signed: Output unsigned, Lhs/Rhs/etc non-real (presumed, not in IEEE)
4732         // IEEE-2012: Table 11-21 and 11.8.1:
4733         //   LHS is self-determined
4734         //   Width: 1 bit out
4735         //   Sign: unsigned out (11.8.1)
4736         if (m_vup->prelim()) {
4737             iterateCheckSizedSelf(nodep, "LHS", nodep->lhsp(), SELF, BOTH);
4738             nodep->dtypeSetBit();
4739         }
4740     }
visit_red_unknown(AstNodeUniop * nodep)4741     void visit_red_unknown(AstNodeUniop* nodep) {
4742         // CALLER: IsUnknown
4743         // Signed: Output unsigned, Lhs/Rhs/etc non-real (presumed, not in IEEE)
4744         // IEEE-2012: Table 11-21 and 11.8.1:
4745         //   LHS is self-determined
4746         //   Width: 1 bit out
4747         //   Sign: unsigned out (11.8.1)
4748         if (m_vup->prelim()) {
4749             userIterateAndNext(nodep->lhsp(), WidthVP(SELF, BOTH).p());
4750             nodep->dtypeSetBit();
4751         }
4752     }
4753 
visit_cmp_eq_gt(AstNodeBiop * nodep,bool realok)4754     void visit_cmp_eq_gt(AstNodeBiop* nodep, bool realok) {
4755         // CALLER: AstEq, AstGt, ..., AstLtS
4756         // Real allowed if and only if real_lhs set
4757         // See IEEE-2012 11.4.4, and 11.8.1:
4758         //   Widths: 1 bit out, width is max of LHS or RHS
4759         //   Sign:  signed compare (not output) if both signed, compare is signed,
4760         //             width mismatches sign extend
4761         //             else, compare is unsigned, **zero-extends**
4762         //   Real:  If either real, other side becomes real and real compare
4763         //   TODO: chandle/class handle/iface handle: WildEq/WildNeq same as Eq/Neq
4764         //   TODO: chandle/class handle/iface handle only allowed to self-compare or against null
4765         //   TODO: chandle/class handle/iface handle no relational compares
4766         UASSERT_OBJ(nodep->rhsp(), nodep, "For binary ops only!");
4767         if (m_vup->prelim()) {
4768             userIterateAndNext(nodep->lhsp(), WidthVP(CONTEXT, PRELIM).p());
4769             userIterateAndNext(nodep->rhsp(), WidthVP(CONTEXT, PRELIM).p());
4770             if (nodep->lhsp()->isDouble() || nodep->rhsp()->isDouble()) {
4771                 if (!realok) nodep->v3error("Real not allowed as operand to in ?== operator");
4772                 if (AstNodeBiop* const newp = replaceWithDVersion(nodep)) {
4773                     VL_DANGLING(nodep);
4774                     nodep = newp;  // Process new node instead
4775                     iterateCheckReal(nodep, "LHS", nodep->lhsp(), FINAL);
4776                     iterateCheckReal(nodep, "RHS", nodep->rhsp(), FINAL);
4777                 }
4778             } else if (nodep->lhsp()->isString() || nodep->rhsp()->isString()) {
4779                 if (AstNodeBiop* const newp = replaceWithNVersion(nodep)) {
4780                     VL_DANGLING(nodep);
4781                     nodep = newp;  // Process new node instead
4782                     iterateCheckString(nodep, "LHS", nodep->lhsp(), FINAL);
4783                     iterateCheckString(nodep, "RHS", nodep->rhsp(), FINAL);
4784                 }
4785             } else {
4786                 const bool signedFl = nodep->lhsp()->isSigned() && nodep->rhsp()->isSigned();
4787                 if (AstNodeBiop* const newp = replaceWithUOrSVersion(nodep, signedFl)) {
4788                     VL_DANGLING(nodep);
4789                     nodep = newp;  // Process new node instead
4790                 }
4791                 const int width = std::max(nodep->lhsp()->width(), nodep->rhsp()->width());
4792                 const int ewidth = std::max(nodep->lhsp()->widthMin(), nodep->rhsp()->widthMin());
4793                 AstNodeDType* const subDTypep
4794                     = nodep->findLogicDType(width, ewidth, VSigning::fromBool(signedFl));
4795                 bool warnOn = true;
4796                 if (!signedFl && width == 32) {
4797                     // Waive on unsigned < or <= if RHS is narrower, since can't give wrong answer
4798                     if ((VN_IS(nodep, Lt) || VN_IS(nodep, Lte))
4799                         && (nodep->lhsp()->width() >= nodep->rhsp()->widthMin())) {
4800                         warnOn = false;
4801                     }
4802                     // Waive on unsigned > or >= if RHS is wider, since can't give wrong answer
4803                     if ((VN_IS(nodep, Gt) || VN_IS(nodep, Gte))
4804                         && (nodep->lhsp()->widthMin() >= nodep->rhsp()->width())) {
4805                         warnOn = false;
4806                     }
4807                 }
4808                 iterateCheck(nodep, "LHS", nodep->lhsp(), CONTEXT, FINAL, subDTypep,
4809                              (signedFl ? EXTEND_LHS : EXTEND_ZERO), warnOn);
4810                 iterateCheck(nodep, "RHS", nodep->rhsp(), CONTEXT, FINAL, subDTypep,
4811                              (signedFl ? EXTEND_LHS : EXTEND_ZERO), warnOn);
4812             }
4813             nodep->dtypeSetBit();
4814         }
4815     }
visit_cmp_real(AstNodeBiop * nodep)4816     void visit_cmp_real(AstNodeBiop* nodep) {
4817         // CALLER: EqD, LtD
4818         // Widths: 1 bit out, lhs width == rhs width
4819         // Signed compare (not output) if both sides signed
4820         // Real if and only if real_allow set
4821         // IEEE, 11.4.4: relational compares (<,>,<=,>=,==,===,!=,!==) use
4822         // "zero padding" on unsigned
4823         UASSERT_OBJ(nodep->rhsp(), nodep, "For binary ops only!");
4824         if (m_vup->prelim()) {
4825             // See similar handling in visit_cmp_eq_gt where created
4826             iterateCheckReal(nodep, "LHS", nodep->lhsp(), BOTH);
4827             iterateCheckReal(nodep, "RHS", nodep->rhsp(), BOTH);
4828             nodep->dtypeSetBit();
4829         }
4830     }
visit_cmp_string(AstNodeBiop * nodep)4831     void visit_cmp_string(AstNodeBiop* nodep) {
4832         // CALLER: EqN, LtN
4833         // Widths: 1 bit out, lhs width == rhs width
4834         // String compare (not output)
4835         // Real if and only if real_allow set
4836         UASSERT_OBJ(nodep->rhsp(), nodep, "For binary ops only!");
4837         if (m_vup->prelim()) {
4838             // See similar handling in visit_cmp_eq_gt where created
4839             iterateCheckString(nodep, "LHS", nodep->lhsp(), BOTH);
4840             iterateCheckString(nodep, "RHS", nodep->rhsp(), BOTH);
4841             nodep->dtypeSetBit();
4842         }
4843     }
4844 
visit_Os32_string(AstNodeUniop * nodep)4845     void visit_Os32_string(AstNodeUniop* nodep) {
4846         // CALLER: LenN
4847         // Widths: 32 bit out
4848         UASSERT_OBJ(nodep->lhsp(), nodep, "For unary ops only!");
4849         if (m_vup->prelim()) {
4850             // See similar handling in visit_cmp_eq_gt where created
4851             iterateCheckString(nodep, "LHS", nodep->lhsp(), BOTH);
4852             nodep->dtypeSetSigned32();
4853         }
4854     }
4855 
visit_negate_not(AstNodeUniop * nodep,bool real_ok)4856     void visit_negate_not(AstNodeUniop* nodep, bool real_ok) {
4857         // CALLER: (real_ok=false) Not
4858         // CALLER: (real_ok=true) Negate
4859         // Signed: From lhs
4860         // IEEE-2012 Table 11-21:
4861         //    Widths: out width = lhs width
4862         UASSERT_OBJ(!nodep->op2p(), nodep, "For unary ops only!");
4863         if (m_vup->prelim()) {
4864             userIterateAndNext(nodep->lhsp(), WidthVP(CONTEXT, PRELIM).p());
4865             if (!real_ok) checkCvtUS(nodep->lhsp());
4866         }
4867         if (real_ok && nodep->lhsp()->isDouble()) {
4868             spliceCvtD(nodep->lhsp());
4869             if (AstNodeUniop* const newp = replaceWithDVersion(nodep)) {
4870                 VL_DANGLING(nodep);
4871                 nodep = newp;  // Process new node instead
4872                 iterateCheckReal(nodep, "LHS", nodep->lhsp(), BOTH);
4873                 nodep->dtypeSetDouble();
4874                 return;
4875             }
4876         } else {
4877             // Note there aren't yet uniops that need version changes
4878             // So no need to call replaceWithUOrSVersion(nodep, nodep->isSigned())
4879         }
4880         if (m_vup->prelim()) nodep->dtypeFrom(nodep->lhsp());
4881         if (m_vup->final()) {
4882             AstNodeDType* const expDTypep = m_vup->dtypeOverridep(nodep->dtypep());
4883             nodep->dtypep(expDTypep);  // Propagate expression type to negation
4884             AstNodeDType* const subDTypep = expDTypep;
4885             iterateCheck(nodep, "LHS", nodep->lhsp(), CONTEXT, FINAL, subDTypep, EXTEND_EXP);
4886         }
4887     }
4888 
visit_signed_unsigned(AstNodeUniop * nodep,VSigning rs_out)4889     void visit_signed_unsigned(AstNodeUniop* nodep, VSigning rs_out) {
4890         // CALLER: Signed, Unsigned
4891         // Width: lhs is self determined width
4892         // See IEEE-2012 6.24.1:
4893         //   Width: Returns packed array, of size $bits(expression).
4894         //   Sign: Output sign is as specified by operation
4895         //   TODO: Type: Two-state if input is two-state, else four-state
4896         UASSERT_OBJ(!nodep->op2p(), nodep, "For unary ops only!");
4897         if (m_vup->prelim()) {
4898             userIterateAndNext(nodep->lhsp(), WidthVP(SELF, PRELIM).p());
4899             checkCvtUS(nodep->lhsp());
4900             const int width = nodep->lhsp()->width();
4901             AstNodeDType* const expDTypep = nodep->findLogicDType(width, width, rs_out);
4902             nodep->dtypep(expDTypep);
4903             AstNodeDType* const subDTypep = expDTypep;
4904             // The child's width is self determined
4905             iterateCheck(nodep, "LHS", nodep->lhsp(), SELF, FINAL, subDTypep, EXTEND_EXP);
4906         }
4907     }
4908 
visit_shift(AstNodeBiop * nodep)4909     void visit_shift(AstNodeBiop* nodep) {
4910         // CALLER: ShiftL, ShiftR, ShiftRS
4911         // Widths: Output width from lhs, rhs<33 bits
4912         // Signed: Output signed iff LHS signed; unary operator
4913         // See IEEE 2012 11.4.10:
4914         //   RHS is self-determined. RHS is always treated as unsigned, has no effect on result.
4915         iterate_shift_prelim(nodep);
4916         nodep->dtypeChgSigned(nodep->lhsp()->isSigned());
4917         const AstNodeBiop* const newp = iterate_shift_final(nodep);
4918         VL_DANGLING(nodep);
4919         if (newp) {}  // Ununused
4920     }
iterate_shift_prelim(AstNodeBiop * nodep)4921     void iterate_shift_prelim(AstNodeBiop* nodep) {
4922         // Shifts
4923         // See IEEE-2012 11.4.10 and Table 11-21.
4924         //   RHS is self-determined. RHS is always treated as unsigned, has no effect on result.
4925         if (m_vup->prelim()) {
4926             userIterateAndNext(nodep->lhsp(), WidthVP(SELF, PRELIM).p());
4927             checkCvtUS(nodep->lhsp());
4928             iterateCheckSizedSelf(nodep, "RHS", nodep->rhsp(), SELF, BOTH);
4929             nodep->dtypeFrom(nodep->lhsp());
4930         }
4931     }
iterate_shift_final(AstNodeBiop * nodep)4932     AstNodeBiop* iterate_shift_final(AstNodeBiop* nodep) {
4933         // Nodep maybe edited
4934         if (m_vup->final()) {
4935             AstNodeDType* const expDTypep = m_vup->dtypeOverridep(nodep->dtypep());
4936             AstNodeDType* const subDTypep = expDTypep;
4937             nodep->dtypeFrom(expDTypep);
4938             // ShiftRS converts to ShiftR, but not vice-versa
4939             if (VN_IS(nodep, ShiftRS)) {
4940                 if (AstNodeBiop* const newp = replaceWithUOrSVersion(nodep, nodep->isSigned())) {
4941                     VL_DANGLING(nodep);
4942                     nodep = newp;  // Process new node instead
4943                 }
4944             }
4945             bool warnOn = true;
4946             // No warning if "X = 1'b1<<N"; assume user is doing what they want
4947             if (nodep->lhsp()->isOne() && VN_IS(nodep->backp(), NodeAssign)) warnOn = false;
4948             iterateCheck(nodep, "LHS", nodep->lhsp(), CONTEXT, FINAL, subDTypep, EXTEND_EXP,
4949                          warnOn);
4950             if (nodep->rhsp()->width() > 32) {
4951                 AstConst* const shiftp = VN_CAST(nodep->rhsp(), Const);
4952                 if (shiftp && shiftp->num().mostSetBitP1() <= 32) {
4953                     // If (number)<<96'h1, then make it into (number)<<32'h1
4954                     V3Number num(shiftp, 32, 0);
4955                     num.opAssign(shiftp->num());
4956                     AstNode* const shiftrhsp = nodep->rhsp();
4957                     nodep->rhsp()->replaceWith(new AstConst(shiftrhsp->fileline(), num));
4958                     VL_DO_DANGLING(shiftrhsp->deleteTree(), shiftrhsp);
4959                 }
4960             }
4961         }
4962         return nodep;  // May edit
4963     }
4964 
visit_boolmath_and_or(AstNodeBiop * nodep)4965     void visit_boolmath_and_or(AstNodeBiop* nodep) {
4966         // CALLER: And, Or, Xor, ...
4967         // Lint widths: out width = lhs width = rhs width
4968         // Signed: if lhs & rhs signed
4969         // IEEE-2012 Table 11-21:
4970         //    Width: max(LHS, RHS)
4971         UASSERT_OBJ(nodep->rhsp(), nodep, "For binary ops only!");
4972         // If errors are off, we need to follow the spec; thus we really need to do the max()
4973         // because the rhs could be larger, and we need to have proper editing to get the widths
4974         // to be the same for our operations.
4975         if (m_vup->prelim()) {  // First stage evaluation
4976             // Determine expression widths only relying on what's in the subops
4977             userIterateAndNext(nodep->lhsp(), WidthVP(CONTEXT, PRELIM).p());
4978             userIterateAndNext(nodep->rhsp(), WidthVP(CONTEXT, PRELIM).p());
4979             checkCvtUS(nodep->lhsp());
4980             checkCvtUS(nodep->rhsp());
4981             const int width = std::max(nodep->lhsp()->width(), nodep->rhsp()->width());
4982             const int mwidth = std::max(nodep->lhsp()->widthMin(), nodep->rhsp()->widthMin());
4983             const bool expSigned = (nodep->lhsp()->isSigned() && nodep->rhsp()->isSigned());
4984             nodep->dtypeChgWidthSigned(width, mwidth, VSigning::fromBool(expSigned));
4985         }
4986         if (m_vup->final()) {
4987             AstNodeDType* const expDTypep = m_vup->dtypeOverridep(nodep->dtypep());
4988             AstNodeDType* const subDTypep = expDTypep;
4989             nodep->dtypeFrom(expDTypep);
4990             // Error report and change sizes for suboperands of this node.
4991             iterateCheck(nodep, "LHS", nodep->lhsp(), CONTEXT, FINAL, subDTypep, EXTEND_EXP);
4992             iterateCheck(nodep, "RHS", nodep->rhsp(), CONTEXT, FINAL, subDTypep, EXTEND_EXP);
4993         }
4994     }
4995 
visit_add_sub_replace(AstNodeBiop * nodep,bool real_ok)4996     void visit_add_sub_replace(AstNodeBiop* nodep, bool real_ok) {
4997         // CALLER: (real_ok=false) AddS, SubS, ...
4998         // CALLER: (real_ok=true) Add, Sub, ...
4999         // Widths: out width = lhs width = rhs width
5000         // Signed: Replace operator with signed operator, or signed to unsigned
5001         // Real: Replace operator with real operator
5002         // IEEE-2012 Table 11-21:
5003         //    Width: max(LHS, RHS)
5004         // If errors are off, we need to follow the spec; thus we really need to do the max()
5005         // because the rhs could be larger, and we need to have proper editing to get the widths
5006         // to be the same for our operations.
5007         //
5008         // if (debug() >= 9) { UINFO(0,"-rus "<<m_vup<<endl); nodep->dumpTree(cout, "-rusin-"); }
5009         if (m_vup->prelim()) {  // First stage evaluation
5010             // Determine expression widths only relying on what's in the subops
5011             userIterateAndNext(nodep->lhsp(), WidthVP(CONTEXT, PRELIM).p());
5012             userIterateAndNext(nodep->rhsp(), WidthVP(CONTEXT, PRELIM).p());
5013             if (!real_ok) {
5014                 checkCvtUS(nodep->lhsp());
5015                 checkCvtUS(nodep->rhsp());
5016             }
5017             if (nodep->lhsp()->isDouble() || nodep->rhsp()->isDouble()) {
5018                 spliceCvtD(nodep->lhsp());
5019                 spliceCvtD(nodep->rhsp());
5020                 if (AstNodeBiop* const newp = replaceWithDVersion(nodep)) {
5021                     VL_DANGLING(nodep);
5022                     nodep = newp;  // Process new node instead
5023                 }
5024                 nodep->dtypeSetDouble();
5025                 iterateCheckReal(nodep, "LHS", nodep->lhsp(), FINAL);
5026                 iterateCheckReal(nodep, "RHS", nodep->rhsp(), FINAL);
5027                 return;
5028             } else {
5029                 const int width = std::max(nodep->lhsp()->width(), nodep->rhsp()->width());
5030                 const int mwidth = std::max(nodep->lhsp()->widthMin(), nodep->rhsp()->widthMin());
5031                 const bool expSigned = (nodep->lhsp()->isSigned() && nodep->rhsp()->isSigned());
5032                 nodep->dtypeChgWidthSigned(width, mwidth, VSigning::fromBool(expSigned));
5033             }
5034         }
5035         if (m_vup->final()) {
5036             // Parent's data type was computed using the max(upper, nodep->dtype)
5037             AstNodeDType* const expDTypep = m_vup->dtypeOverridep(nodep->dtypep());
5038             AstNodeDType* const subDTypep = expDTypep;
5039             nodep->dtypeFrom(expDTypep);
5040             // We don't use LHS && RHS -- unspecified language corner, see t_math_signed5 test
5041             // bool expSigned = (nodep->lhsp()->isSigned() && nodep->rhsp()->isSigned());
5042             if (AstNodeBiop* const newp = replaceWithUOrSVersion(nodep, expDTypep->isSigned())) {
5043                 VL_DANGLING(nodep);
5044                 nodep = newp;  // Process new node instead
5045             }
5046             // Some warning suppressions
5047             bool lhsWarn = true;
5048             bool rhsWarn = true;
5049             if (VN_IS(nodep, Add) || VN_IS(nodep, Sub)) {
5050                 // Warn if user wants extra bit from carry
5051                 if (subDTypep->widthMin() == (nodep->lhsp()->widthMin() + 1)) lhsWarn = false;
5052                 if (subDTypep->widthMin() == (nodep->rhsp()->widthMin() + 1)) rhsWarn = false;
5053             } else if (VN_IS(nodep, Mul) || VN_IS(nodep, MulS)) {
5054                 if (subDTypep->widthMin() >= (nodep->lhsp()->widthMin())) lhsWarn = false;
5055                 if (subDTypep->widthMin() >= (nodep->rhsp()->widthMin())) rhsWarn = false;
5056             }
5057             // Final call, so make sure children check their sizes
5058             // Error report and change sizes for suboperands of this node.
5059             iterateCheck(nodep, "LHS", nodep->lhsp(), CONTEXT, FINAL, subDTypep, EXTEND_EXP,
5060                          lhsWarn);
5061             iterateCheck(nodep, "RHS", nodep->rhsp(), CONTEXT, FINAL, subDTypep, EXTEND_EXP,
5062                          rhsWarn);
5063         }
5064         // if (debug() >= 9) nodep->dumpTree(cout, "-rusou-");
5065     }
visit_real_add_sub(AstNodeBiop * nodep)5066     void visit_real_add_sub(AstNodeBiop* nodep) {
5067         // CALLER: AddD, MulD, ...
5068         if (m_vup->prelim()) {  // First stage evaluation
5069             // Note similar steps in visit_add_sub_replace promotion to double
5070             iterateCheckReal(nodep, "LHS", nodep->lhsp(), BOTH);
5071             iterateCheckReal(nodep, "RHS", nodep->rhsp(), BOTH);
5072             nodep->dtypeSetDouble();
5073         }
5074     }
visit_real_neg_ceil(AstNodeUniop * nodep)5075     void visit_real_neg_ceil(AstNodeUniop* nodep) {
5076         // CALLER: Negate, Ceil, Log, ...
5077         if (m_vup->prelim()) {  // First stage evaluation
5078             // See alsl visit_negate_not conversion
5079             iterateCheckReal(nodep, "LHS", nodep->lhsp(), BOTH);
5080             nodep->dtypeSetDouble();
5081         }
5082     }
5083 
5084     //----------------------------------------------------------------------
5085     // LOWER LEVEL WIDTH METHODS  (none iterate)
5086 
widthBad(AstNode * nodep,AstNodeDType * expDTypep)5087     bool widthBad(AstNode* nodep, AstNodeDType* expDTypep) {
5088         const int expWidth = expDTypep->width();
5089         int expWidthMin = expDTypep->widthMin();
5090         UASSERT_OBJ(nodep->dtypep(), nodep,
5091                     "Under node " << nodep->prettyTypeName()
5092                                   << " has no dtype?? Missing Visitor func?");
5093         UASSERT_OBJ(nodep->width() != 0, nodep,
5094                     "Under node " << nodep->prettyTypeName()
5095                                   << " has no expected width?? Missing Visitor func?");
5096         UASSERT_OBJ(expWidth != 0, nodep,
5097                     "Node " << nodep->prettyTypeName()
5098                             << " has no expected width?? Missing Visitor func?");
5099         if (expWidthMin == 0) expWidthMin = expWidth;
5100         if (nodep->dtypep()->width() == expWidth) return false;
5101         if (nodep->dtypep()->widthSized() && nodep->width() != expWidthMin) return true;
5102         if (!nodep->dtypep()->widthSized() && nodep->widthMin() > expWidthMin) return true;
5103         return false;
5104     }
5105 
fixWidthExtend(AstNode * nodep,AstNodeDType * expDTypep,ExtendRule extendRule)5106     void fixWidthExtend(AstNode* nodep, AstNodeDType* expDTypep, ExtendRule extendRule) {
5107         // Fix the width mismatch by extending or truncating bits
5108         // *ONLY* call this from checkWidth()
5109         // Truncation is rarer, but can occur:  parameter [3:0] FOO = 64'h12312;
5110         // A(CONSTwide)+B becomes  A(CONSTwidened)+B
5111         // A(somewide)+B  becomes  A(TRUNC(somewide,width))+B
5112         //                    or   A(EXTRACT(somewide,width,0))+B
5113         // Sign extension depends on the type of the *present*
5114         // node, while the output dtype is the *expected* sign.
5115         // It is reasonable to have sign extension with unsigned output,
5116         // for example $unsigned(a)+$signed(b), the SIGNED(B) will be unsigned dtype out
5117         UINFO(4, "  widthExtend_(r=" << extendRule << ") old: " << nodep << endl);
5118         if (extendRule == EXTEND_OFF) return;
5119         AstConst* const constp = VN_CAST(nodep, Const);
5120         const int expWidth = expDTypep->width();
5121         if (constp && !constp->num().isNegative()) {
5122             // Save later constant propagation work, just right-size it.
5123             V3Number num(nodep, expWidth);
5124             num.opAssign(constp->num());
5125             num.isSigned(false);
5126             AstNode* const newp = new AstConst(nodep->fileline(), num);
5127             constp->replaceWith(newp);
5128             VL_DO_DANGLING(pushDeletep(constp), constp);
5129             VL_DANGLING(nodep);
5130             nodep = newp;
5131         } else if (expWidth < nodep->width()) {
5132             // Trunc - Extract
5133             AstNRelinker linker;
5134             nodep->unlinkFrBack(&linker);
5135             AstNode* const newp = new AstSel(nodep->fileline(), nodep, 0, expWidth);
5136             newp->didWidth(true);  // Don't replace dtype with unsigned
5137             linker.relink(newp);
5138             nodep = newp;
5139         } else {
5140             // Extend
5141             AstNRelinker linker;
5142             nodep->unlinkFrBack(&linker);
5143             bool doSigned = false;
5144             switch (extendRule) {
5145             case EXTEND_ZERO: doSigned = false; break;
5146             case EXTEND_EXP: doSigned = nodep->isSigned() && expDTypep->isSigned(); break;
5147             case EXTEND_LHS: doSigned = nodep->isSigned(); break;
5148             default: nodep->v3fatalSrc("bad case");
5149             }
5150             AstNode* const newp
5151                 = (doSigned ? static_cast<AstNode*>(new AstExtendS{nodep->fileline(), nodep})
5152                             : static_cast<AstNode*>(new AstExtend{nodep->fileline(), nodep}));
5153             linker.relink(newp);
5154             nodep = newp;
5155         }
5156         if (expDTypep->isDouble() && !nodep->isDouble()) {
5157             // For AstVar init() among others
5158             // TODO do all to-real and to-integer conversions in this function
5159             // rather than in callers
5160             AstNode* const newp = spliceCvtD(nodep);
5161             nodep = newp;
5162         }
5163         nodep->dtypeFrom(expDTypep);
5164         UINFO(4, "             _new: " << nodep << endl);
5165     }
5166 
fixWidthReduce(AstNode * nodep)5167     void fixWidthReduce(AstNode* nodep) {
5168         // Fix the width mismatch by adding a reduction OR operator
5169         // IF (A(CONSTwide)) becomes  IF (A(CONSTreduced))
5170         // IF (A(somewide))  becomes  IF (A(REDOR(somewide)))
5171         // Attempt to fix it quietly
5172         const int expWidth = 1;
5173         const int expSigned = false;
5174         UINFO(4, "  widthReduce_old: " << nodep << endl);
5175         AstConst* const constp = VN_CAST(nodep, Const);
5176         if (constp) {
5177             V3Number num(nodep, expWidth);
5178             num.opRedOr(constp->num());
5179             num.isSigned(expSigned);
5180             AstNode* const newp = new AstConst(nodep->fileline(), num);
5181             constp->replaceWith(newp);
5182             VL_DO_DANGLING(constp->deleteTree(), constp);
5183             VL_DANGLING(nodep);
5184             nodep = newp;
5185         } else {
5186             AstNRelinker linker;
5187             nodep->unlinkFrBack(&linker);
5188             AstNode* const newp = new AstRedOr(nodep->fileline(), nodep);
5189             linker.relink(newp);
5190             nodep = newp;
5191         }
5192         nodep->dtypeChgWidthSigned(expWidth, expWidth, VSigning::fromBool(expSigned));
5193         UINFO(4, "             _new: " << nodep << endl);
5194     }
5195 
fixAutoExtend(AstNode * & nodepr,int expWidth)5196     bool fixAutoExtend(AstNode*& nodepr, int expWidth) {
5197         // For SystemVerilog '0,'1,'x,'z, autoextend and don't warn
5198         if (AstConst* const constp = VN_CAST(nodepr, Const)) {
5199             if (constp->num().autoExtend() && !constp->num().sized() && constp->width() == 1) {
5200                 // Make it the proper size.  Careful of proper extension of 0's/1's
5201                 V3Number num(constp, expWidth);
5202                 num.opRepl(constp->num(), expWidth);  // {width{'1}}
5203                 AstNode* const newp = new AstConst(constp->fileline(), num);
5204                 // Spec says always unsigned with proper width
5205                 if (debug() > 4) constp->dumpTree(cout, "  fixAutoExtend_old: ");
5206                 if (debug() > 4) newp->dumpTree(cout, "               _new: ");
5207                 constp->replaceWith(newp);
5208                 VL_DO_DANGLING(constp->deleteTree(), constp);
5209                 // Tell caller the new constp, and that we changed it.
5210                 nodepr = newp;
5211                 return true;
5212             }
5213             // X/Z also upper bit extend.  In pre-SV only to 32-bits, SV forever.
5214             else if (!constp->num().sized()
5215                      // Make it the proper size.  Careful of proper extension of 0's/1's
5216                      && expWidth > 32 && constp->num().isMsbXZ()) {
5217                 constp->v3warn(WIDTH, "Unsized constant being X/Z extended to "
5218                                           << expWidth << " bits: " << constp->prettyName());
5219                 V3Number num(constp, expWidth);
5220                 num.opExtendXZ(constp->num(), constp->width());
5221                 AstNode* const newp = new AstConst(constp->fileline(), num);
5222                 // Spec says always unsigned with proper width
5223                 if (debug() > 4) constp->dumpTree(cout, "  fixUnszExtend_old: ");
5224                 if (debug() > 4) newp->dumpTree(cout, "               _new: ");
5225                 constp->replaceWith(newp);
5226                 VL_DO_DANGLING(constp->deleteTree(), constp);
5227                 // Tell caller the new constp, and that we changed it.
5228                 nodepr = newp;
5229                 return true;
5230             }
5231         }
5232         return false;  // No change
5233     }
5234 
similarDTypeRecurse(AstNodeDType * node1p,AstNodeDType * node2p)5235     static bool similarDTypeRecurse(AstNodeDType* node1p, AstNodeDType* node2p) {
5236         return node1p->skipRefp()->similarDType(node2p->skipRefp());
5237     }
iterateCheckFileDesc(AstNode * nodep,AstNode * underp,Stage stage)5238     void iterateCheckFileDesc(AstNode* nodep, AstNode* underp, Stage stage) {
5239         UASSERT_OBJ(stage == BOTH, nodep, "Bad call");
5240         // underp may change as a result of replacement
5241         underp = userIterateSubtreeReturnEdits(underp, WidthVP(SELF, PRELIM).p());
5242         AstNodeDType* const expDTypep = underp->findUInt32DType();
5243         underp
5244             = iterateCheck(nodep, "file_descriptor", underp, SELF, FINAL, expDTypep, EXTEND_EXP);
5245         if (underp) {}  // cppcheck
5246     }
iterateCheckSigned32(AstNode * nodep,const char * side,AstNode * underp,Stage stage)5247     void iterateCheckSigned32(AstNode* nodep, const char* side, AstNode* underp, Stage stage) {
5248         // Coerce child to signed32 if not already. Child is self-determined
5249         // underp may change as a result of replacement
5250         if (stage & PRELIM) {
5251             underp = userIterateSubtreeReturnEdits(underp, WidthVP(SELF, PRELIM).p());
5252         }
5253         if (stage & FINAL) {
5254             AstNodeDType* const expDTypep = nodep->findSigned32DType();
5255             underp = iterateCheck(nodep, side, underp, SELF, FINAL, expDTypep, EXTEND_EXP);
5256         }
5257         if (underp) {}  // cppcheck
5258     }
iterateCheckReal(AstNode * nodep,const char * side,AstNode * underp,Stage stage)5259     void iterateCheckReal(AstNode* nodep, const char* side, AstNode* underp, Stage stage) {
5260         // Coerce child to real if not already. Child is self-determined
5261         // e.g. nodep=ADDD, underp=ADD in ADDD(ADD(a,b), real-CONST)
5262         // Don't need separate PRELIM and FINAL(double) calls;
5263         // as if resolves to double, the BOTH correctly resolved double,
5264         // otherwise self-determined was correct
5265         // underp may change as a result of replacement
5266         if (stage & PRELIM) {
5267             underp = userIterateSubtreeReturnEdits(underp, WidthVP(SELF, PRELIM).p());
5268         }
5269         if (stage & FINAL) {
5270             AstNodeDType* const expDTypep = nodep->findDoubleDType();
5271             underp = iterateCheck(nodep, side, underp, SELF, FINAL, expDTypep, EXTEND_EXP);
5272         }
5273         if (underp) {}  // cppcheck
5274     }
iterateCheckString(AstNode * nodep,const char * side,AstNode * underp,Stage stage)5275     void iterateCheckString(AstNode* nodep, const char* side, AstNode* underp, Stage stage) {
5276         if (stage & PRELIM) {
5277             underp = userIterateSubtreeReturnEdits(underp, WidthVP(SELF, PRELIM).p());
5278         }
5279         if (stage & FINAL) {
5280             AstNodeDType* const expDTypep = nodep->findStringDType();
5281             underp = iterateCheck(nodep, side, underp, SELF, FINAL, expDTypep, EXTEND_EXP);
5282         }
5283         if (underp) {}  // cppcheck
5284     }
iterateCheckTyped(AstNode * nodep,const char * side,AstNode * underp,AstNodeDType * expDTypep,Stage stage)5285     void iterateCheckTyped(AstNode* nodep, const char* side, AstNode* underp,
5286                            AstNodeDType* expDTypep, Stage stage) {
5287         if (stage & PRELIM) {
5288             underp = userIterateSubtreeReturnEdits(underp, WidthVP(expDTypep, PRELIM).p());
5289         }
5290         if (stage & FINAL) {
5291             underp = iterateCheck(nodep, side, underp, SELF, FINAL, expDTypep, EXTEND_EXP);
5292         }
5293         if (underp) {}  // cppcheck
5294     }
iterateCheckSizedSelf(AstNode * nodep,const char * side,AstNode * underp,Determ determ,Stage stage)5295     void iterateCheckSizedSelf(AstNode* nodep, const char* side, AstNode* underp, Determ determ,
5296                                Stage stage) {
5297         // Coerce child to any sized-number data type; child is self-determined
5298         // i.e. isolated from expected type.
5299         // e.g. nodep=CONCAT, underp=lhs in CONCAT(lhs,rhs)
5300         UASSERT_OBJ(determ == SELF, nodep, "Bad call");
5301         UASSERT_OBJ(stage == FINAL || stage == BOTH, nodep, "Bad call");
5302         // underp may change as a result of replacement
5303         if (stage & PRELIM) {
5304             underp = userIterateSubtreeReturnEdits(underp, WidthVP(SELF, PRELIM).p());
5305         }
5306         underp = checkCvtUS(underp);
5307         AstNodeDType* const expDTypep = underp->dtypep();
5308         underp = iterateCheck(nodep, side, underp, SELF, FINAL, expDTypep, EXTEND_EXP);
5309         if (underp) {}  // cppcheck
5310     }
iterateCheckAssign(AstNode * nodep,const char * side,AstNode * rhsp,Stage stage,AstNodeDType * lhsDTypep)5311     void iterateCheckAssign(AstNode* nodep, const char* side, AstNode* rhsp, Stage stage,
5312                             AstNodeDType* lhsDTypep) {
5313         // Check using assignment-like context rules
5314         // if (debug()) nodep->dumpTree(cout, "-checkass: ");
5315         UASSERT_OBJ(stage == FINAL, nodep, "Bad width call");
5316         // We iterate and size the RHS based on the result of RHS evaluation
5317         const bool lhsStream
5318             = (VN_IS(nodep, NodeAssign) && VN_IS(VN_AS(nodep, NodeAssign)->lhsp(), NodeStream));
5319         rhsp = iterateCheck(nodep, side, rhsp, ASSIGN, FINAL, lhsDTypep,
5320                             lhsStream ? EXTEND_OFF : EXTEND_LHS);
5321         // if (debug()) nodep->dumpTree(cout, "-checkout: ");
5322         if (rhsp) {}  // cppcheck
5323     }
5324 
iterateCheckBool(AstNode * nodep,const char * side,AstNode * underp,Stage stage)5325     void iterateCheckBool(AstNode* nodep, const char* side, AstNode* underp, Stage stage) {
5326         UASSERT_OBJ(stage == BOTH, nodep,
5327                     "Bad call");  // Booleans always self-determined so do BOTH at once
5328         // Underp is used in a self-determined but boolean context, reduce a
5329         // multibit number to one bit
5330         // stage is always BOTH so not passed as argument
5331         // underp may change as a result of replacement
5332         UASSERT_OBJ(underp, nodep, "Node has no type");
5333         underp = userIterateSubtreeReturnEdits(underp, WidthVP(SELF, BOTH).p());
5334         UASSERT_OBJ(underp && underp->dtypep(), nodep,
5335                     "Node has no type");  // Perhaps forgot to do a prelim visit on it?
5336         //
5337         // For DOUBLE under a logical op, add implied test against zero, never a warning
5338         if (underp && underp->isDouble()) {
5339             UINFO(6, "   spliceCvtCmpD0: " << underp << endl);
5340             AstNRelinker linker;
5341             underp->unlinkFrBack(&linker);
5342             AstNode* const newp
5343                 = new AstNeqD(nodep->fileline(), underp,
5344                               new AstConst(nodep->fileline(), AstConst::RealDouble(), 0.0));
5345             linker.relink(newp);
5346         } else if (VN_IS(underp->dtypep(), ClassRefDType)
5347                    || (VN_IS(underp->dtypep(), BasicDType)
5348                        && VN_AS(underp->dtypep(), BasicDType)->keyword()
5349                               == AstBasicDTypeKwd::CHANDLE)) {
5350             // Allow warning-free "if (handle)"
5351             VL_DO_DANGLING(fixWidthReduce(underp), underp);  // Changed
5352         } else if (!underp->dtypep()->basicp()) {
5353             nodep->v3error("Logical operator " << nodep->prettyTypeName()
5354                                                << " expects a non-complex data type on the "
5355                                                << side << ".");
5356             underp->replaceWith(new AstConst(nodep->fileline(), AstConst::BitFalse()));
5357             VL_DO_DANGLING(pushDeletep(underp), underp);
5358         } else {
5359             const bool bad = widthBad(underp, nodep->findBitDType());
5360             if (bad) {
5361                 {  // if (warnOn), but not needed here
5362                     if (debug() > 4) nodep->backp()->dumpTree(cout, "  back: ");
5363                     nodep->v3warn(WIDTH, "Logical operator "
5364                                              << nodep->prettyTypeName() << " expects 1 bit on the "
5365                                              << side << ", but " << side << "'s "
5366                                              << underp->prettyTypeName() << " generates "
5367                                              << underp->width()
5368                                              << (underp->width() != underp->widthMin()
5369                                                      ? " or " + cvtToStr(underp->widthMin())
5370                                                      : "")
5371                                              << " bits.");
5372                 }
5373                 VL_DO_DANGLING(fixWidthReduce(underp), underp);  // Changed
5374             }
5375         }
5376     }
5377 
iterateCheck(AstNode * nodep,const char * side,AstNode * underp,Determ determ,Stage stage,AstNodeDType * expDTypep,ExtendRule extendRule,bool warnOn=true)5378     AstNode* iterateCheck(AstNode* nodep, const char* side, AstNode* underp, Determ determ,
5379                           Stage stage, AstNodeDType* expDTypep, ExtendRule extendRule,
5380                           bool warnOn = true) {
5381         // Perform data type check on underp, which is underneath nodep used for error reporting
5382         // Returns the new underp
5383         // Conversion to/from doubles and integers are before iterating.
5384         UASSERT_OBJ(stage == FINAL, nodep, "Bad state to iterateCheck");
5385         UASSERT_OBJ(underp && underp->dtypep(), nodep,
5386                     "Node has no type");  // Perhaps forgot to do a prelim visit on it?
5387         if (VN_IS(underp, NodeDType)) {  // Note the node itself, not node's data type
5388             // Must be near top of these checks as underp->dtypep() will look normal
5389             underp->v3error(ucfirst(nodep->prettyOperatorName())
5390                             << " expected non-datatype " << side << " but '" << underp->name()
5391                             << "' is a datatype.");
5392         } else if (expDTypep == underp->dtypep()) {  // Perfect
5393             underp = userIterateSubtreeReturnEdits(underp, WidthVP(expDTypep, FINAL).p());
5394         } else if (expDTypep->isDouble() && underp->isDouble()) {  // Also good
5395             underp = userIterateSubtreeReturnEdits(underp, WidthVP(expDTypep, FINAL).p());
5396         } else if (expDTypep->isDouble() && !underp->isDouble()) {
5397             AstNode* const oldp
5398                 = underp;  // Need FINAL on children; otherwise splice would block it
5399             underp = spliceCvtD(underp);
5400             underp = userIterateSubtreeReturnEdits(oldp, WidthVP(SELF, FINAL).p());
5401         } else if (!expDTypep->isDouble() && underp->isDouble()) {
5402             AstNode* const oldp
5403                 = underp;  // Need FINAL on children; otherwise splice would block it
5404             underp = spliceCvtS(underp, true, expDTypep->width());  // Round RHS
5405             underp = userIterateSubtreeReturnEdits(oldp, WidthVP(SELF, FINAL).p());
5406         } else if (expDTypep->isString() && !underp->dtypep()->isString()) {
5407             AstNode* const oldp
5408                 = underp;  // Need FINAL on children; otherwise splice would block it
5409             underp = spliceCvtString(underp);
5410             underp = userIterateSubtreeReturnEdits(oldp, WidthVP(SELF, FINAL).p());
5411         } else {
5412             const AstBasicDType* const expBasicp = expDTypep->basicp();
5413             const AstBasicDType* const underBasicp = underp->dtypep()->basicp();
5414             if (expBasicp && underBasicp) {
5415                 AstNodeDType* subDTypep = expDTypep;
5416                 // We then iterate FINAL before width fixes, as if the under-operation
5417                 // is e.g. an ADD, the ADD will auto-adjust to the proper data type
5418                 // or if another operation e.g. ATOI will not.
5419                 if (determ == SELF) {
5420                     underp = userIterateSubtreeReturnEdits(underp, WidthVP(SELF, FINAL).p());
5421                 } else if (determ == ASSIGN) {
5422                     // IEEE: Signedness is solely determined by the RHS
5423                     // (underp), not by the LHS (expDTypep)
5424                     if (underp->isSigned() != subDTypep->isSigned()
5425                         || underp->width() != subDTypep->width()) {
5426                         subDTypep = nodep->findLogicDType(
5427                             std::max(subDTypep->width(), underp->width()),
5428                             std::max(subDTypep->widthMin(), underp->widthMin()),
5429                             VSigning::fromBool(underp->isSigned()));
5430                         UINFO(9, "Assignment of opposite-signed RHS to LHS: " << nodep << endl);
5431                     }
5432                     underp = userIterateSubtreeReturnEdits(underp, WidthVP(subDTypep, FINAL).p());
5433                 } else {
5434                     underp = userIterateSubtreeReturnEdits(underp, WidthVP(subDTypep, FINAL).p());
5435                 }
5436                 // Note the check uses the expected size, not the child's subDTypep as we want the
5437                 // child node's width to end up correct for the assignment (etc)
5438                 widthCheckSized(nodep, side, underp, expDTypep, extendRule, warnOn);
5439             } else if (!VN_IS(expDTypep, IfaceRefDType)
5440                        && VN_IS(underp->dtypep(), IfaceRefDType)) {
5441                 underp->v3error(ucfirst(nodep->prettyOperatorName())
5442                                 << " expected non-interface on " << side << " but '"
5443                                 << underp->name() << "' is an interface.");
5444             } else {
5445                 // Hope it just works out (perhaps a cast will deal with it)
5446                 underp = userIterateSubtreeReturnEdits(underp, WidthVP(expDTypep, FINAL).p());
5447             }
5448         }
5449         return underp;
5450     }
5451 
widthCheckSized(AstNode * nodep,const char * side,AstNode * underp,AstNodeDType * expDTypep,ExtendRule extendRule,bool warnOn=true)5452     void widthCheckSized(AstNode* nodep, const char* side,
5453                          AstNode* underp,  // Node to be checked or have typecast added in front of
5454                          AstNodeDType* expDTypep, ExtendRule extendRule, bool warnOn = true) {
5455         // Issue warnings on sized number width mismatches, then do appropriate size extension
5456         // Generally iterateCheck is what is wanted instead of this
5457         // UINFO(9,"wchk "<<side<<endl<<"  "<<nodep<<endl<<"  "<<underp<<endl<<"  e="<<expDTypep<<"
5458         // i"<<warnOn<<endl);
5459         const AstBasicDType* const expBasicp = expDTypep->basicp();
5460         const AstBasicDType* const underBasicp = underp->dtypep()->basicp();
5461         if (expDTypep == underp->dtypep()) {
5462             return;  // Same type must match
5463         } else if (!expBasicp || expBasicp->isDouble() || !underBasicp
5464                    || underBasicp->isDouble()) {
5465             // This is perhaps a v3fatalSrc as we should have checked the types
5466             // before calling widthCheck, but we may have missed a non-sized
5467             // check in earlier code, so might as well assume it is the users'
5468             // fault.
5469             nodep->v3error(ucfirst(nodep->prettyOperatorName())
5470                            << " expected non-complex non-double " << side << " in width check");
5471 #if VL_DEBUG
5472             nodep->v3fatalSrc("widthCheckSized should not be called on doubles/complex types");
5473 #endif
5474             return;
5475         } else {
5476             const int expWidth = expDTypep->width();
5477             int expWidthMin = expDTypep->widthMin();
5478             if (expWidthMin == 0) expWidthMin = expWidth;
5479             const bool bad = widthBad(underp, expDTypep);
5480             if ((bad || underp->width() != expWidth) && fixAutoExtend(underp /*ref*/, expWidth)) {
5481                 underp = nullptr;  // Changes underp
5482                 return;
5483             }
5484             if (VN_IS(underp, Const) && VN_AS(underp, Const)->num().isFromString()
5485                 && expWidth > underp->width()
5486                 && (((expWidth - underp->width()) % 8) == 0)) {  // At least it's character sized
5487                 // reg [31:0] == "foo" we'll consider probably fine.
5488                 // Maybe this should be a special warning?  Not for now.
5489                 warnOn = false;
5490             }
5491             if ((VN_IS(nodep, Add) && underp->width() == 1 && underp->isOne())
5492                 || (VN_IS(nodep, Sub) && underp->width() == 1 && underp->isOne()
5493                     && 0 == strcmp(side, "RHS"))) {
5494                 // "foo + 1'b1", or "foo - 1'b1" are very common, people assume
5495                 // they extend correctly
5496                 warnOn = false;
5497             }
5498             if (bad && warnOn) {
5499                 if (debug() > 4) nodep->backp()->dumpTree(cout, "  back: ");
5500                 nodep->v3warn(
5501                     WIDTH, ucfirst(nodep->prettyOperatorName())
5502                                << " expects " << expWidth
5503                                << (expWidth != expWidthMin ? " or " + cvtToStr(expWidthMin) : "")
5504                                << " bits on the " << side << ", but " << side << "'s "
5505                                << underp->prettyTypeName() << " generates " << underp->width()
5506                                << (underp->width() != underp->widthMin()
5507                                        ? " or " + cvtToStr(underp->widthMin())
5508                                        : "")
5509                                << " bits.");
5510             }
5511             if (bad || underp->width() != expWidth) {
5512                 // If we're in an NodeAssign, don't truncate the RHS if the LHS is
5513                 // a NodeStream. The streaming operator changes the rules regarding
5514                 // which bits to truncate.
5515                 const AstNodeAssign* assignp = VN_CAST(nodep, NodeAssign);
5516                 const AstPin* pinp = VN_CAST(nodep, Pin);
5517                 if (assignp && VN_IS(assignp->lhsp(), NodeStream)) {
5518                 } else if (pinp && pinp->modVarp()->direction() != VDirection::INPUT) {
5519                     // V3Inst::pinReconnectSimple must deal
5520                     UINFO(5, "pinInSizeMismatch: " << pinp);
5521                 } else {
5522                     VL_DO_DANGLING(fixWidthExtend(underp, expDTypep, extendRule), underp);
5523                 }
5524             }
5525         }
5526     }
5527 
5528     //----------------------------------------------------------------------
5529     // SIGNED/DOUBLE METHODS
5530 
checkCvtUS(AstNode * nodep)5531     AstNode* checkCvtUS(AstNode* nodep) {
5532         if (nodep && nodep->isDouble()) {
5533             nodep->v3error("Expected integral (non-" << nodep->dtypep()->prettyDTypeName()
5534                                                      << ") input to "
5535                                                      << nodep->backp()->prettyTypeName());
5536             nodep = spliceCvtS(nodep, true, 32);
5537         }
5538         return nodep;
5539     }
5540 
spliceCvtD(AstNode * nodep)5541     AstNode* spliceCvtD(AstNode* nodep) {
5542         // For integer used in REAL context, convert to real
5543         // We don't warn here, "2.0 * 2" is common and reasonable
5544         if (nodep && !nodep->dtypep()->skipRefp()->isDouble()) {
5545             UINFO(6, "   spliceCvtD: " << nodep << endl);
5546             AstNRelinker linker;
5547             nodep->unlinkFrBack(&linker);
5548             AstNode* newp;
5549             if (nodep->dtypep()->skipRefp()->isSigned()) {
5550                 newp = new AstISToRD(nodep->fileline(), nodep);
5551             } else {
5552                 newp = new AstIToRD(nodep->fileline(), nodep);
5553             }
5554             linker.relink(newp);
5555             return newp;
5556         } else {
5557             return nodep;
5558         }
5559     }
spliceCvtS(AstNode * nodep,bool warnOn,int width)5560     AstNode* spliceCvtS(AstNode* nodep, bool warnOn, int width) {
5561         // IEEE-2012 11.8.1: Signed: Type coercion creates signed
5562         // 11.8.2: Argument to convert is self-determined
5563         if (nodep && nodep->dtypep()->skipRefp()->isDouble()) {
5564             UINFO(6, "   spliceCvtS: " << nodep << endl);
5565             AstNRelinker linker;
5566             nodep->unlinkFrBack(&linker);
5567             if (const AstConst* const constp = VN_CAST(nodep, Const)) {
5568                 // We convert to/from vlsint32 rather than use floor() as want to make sure is
5569                 // representable in integer's number of bits
5570                 if (constp->isDouble()
5571                     && v3EpsilonEqual(
5572                         constp->num().toDouble(),
5573                         static_cast<double>(static_cast<vlsint32_t>(constp->num().toDouble())))) {
5574                     warnOn = false;
5575                 }
5576             }
5577             if (warnOn) nodep->v3warn(REALCVT, "Implicit conversion of real to integer");
5578             AstNode* const newp = new AstRToIRoundS(nodep->fileline(), nodep);
5579             linker.relink(newp);
5580             newp->dtypeSetBitSized(width, VSigning::SIGNED);
5581             return newp;
5582         } else {
5583             return nodep;
5584         }
5585     }
spliceCvtString(AstNode * nodep)5586     AstNode* spliceCvtString(AstNode* nodep) {
5587         // IEEE-2012 11.8.1: Signed: Type coercion creates signed
5588         // 11.8.2: Argument to convert is self-determined
5589         if (nodep && !(nodep->dtypep()->basicp() && nodep->dtypep()->basicp()->isString())) {
5590             UINFO(6, "   spliceCvtString: " << nodep << endl);
5591             AstNRelinker linker;
5592             nodep->unlinkFrBack(&linker);
5593             AstNode* const newp = new AstCvtPackString(nodep->fileline(), nodep);
5594             linker.relink(newp);
5595             return newp;
5596         } else {
5597             return nodep;
5598         }
5599     }
replaceWithUOrSVersion(AstNodeBiop * nodep,bool signedFlavorNeeded)5600     AstNodeBiop* replaceWithUOrSVersion(AstNodeBiop* nodep, bool signedFlavorNeeded) {
5601         // Given a signed/unsigned node type, create the opposite type
5602         // Return new node or nullptr if nothing
5603         if (signedFlavorNeeded == nodep->signedFlavor()) return nullptr;
5604         if (!nodep->dtypep()) nodep->dtypeFrom(nodep->lhsp());
5605         // To simplify callers, some node types don't need to change
5606         switch (nodep->type()) {
5607         case AstType::atEq: nodep->dtypeChgSigned(signedFlavorNeeded); return nullptr;
5608         case AstType::atNeq: nodep->dtypeChgSigned(signedFlavorNeeded); return nullptr;
5609         case AstType::atEqCase: nodep->dtypeChgSigned(signedFlavorNeeded); return nullptr;
5610         case AstType::atNeqCase: nodep->dtypeChgSigned(signedFlavorNeeded); return nullptr;
5611         case AstType::atEqWild: nodep->dtypeChgSigned(signedFlavorNeeded); return nullptr;
5612         case AstType::atNeqWild: nodep->dtypeChgSigned(signedFlavorNeeded); return nullptr;
5613         case AstType::atAdd: nodep->dtypeChgSigned(signedFlavorNeeded); return nullptr;
5614         case AstType::atSub: nodep->dtypeChgSigned(signedFlavorNeeded); return nullptr;
5615         case AstType::atShiftL: nodep->dtypeChgSigned(signedFlavorNeeded); return nullptr;
5616         default: break;
5617         }
5618         FileLine* const fl = nodep->fileline();
5619         AstNode* const lhsp = nodep->lhsp()->unlinkFrBack();
5620         AstNode* const rhsp = nodep->rhsp()->unlinkFrBack();
5621         AstNodeBiop* newp = nullptr;
5622         switch (nodep->type()) {
5623         case AstType::atGt: newp = new AstGtS(fl, lhsp, rhsp); break;
5624         case AstType::atGtS: newp = new AstGt(fl, lhsp, rhsp); break;
5625         case AstType::atGte: newp = new AstGteS(fl, lhsp, rhsp); break;
5626         case AstType::atGteS: newp = new AstGte(fl, lhsp, rhsp); break;
5627         case AstType::atLt: newp = new AstLtS(fl, lhsp, rhsp); break;
5628         case AstType::atLtS: newp = new AstLt(fl, lhsp, rhsp); break;
5629         case AstType::atLte: newp = new AstLteS(fl, lhsp, rhsp); break;
5630         case AstType::atLteS: newp = new AstLte(fl, lhsp, rhsp); break;
5631         case AstType::atDiv: newp = new AstDivS(fl, lhsp, rhsp); break;
5632         case AstType::atDivS: newp = new AstDiv(fl, lhsp, rhsp); break;
5633         case AstType::atModDiv: newp = new AstModDivS(fl, lhsp, rhsp); break;
5634         case AstType::atModDivS: newp = new AstModDiv(fl, lhsp, rhsp); break;
5635         case AstType::atMul: newp = new AstMulS(fl, lhsp, rhsp); break;
5636         case AstType::atMulS: newp = new AstMul(fl, lhsp, rhsp); break;
5637         case AstType::atShiftR: newp = new AstShiftRS(fl, lhsp, rhsp); break;
5638         case AstType::atShiftRS: newp = new AstShiftR(fl, lhsp, rhsp); break;
5639         default:  // LCOV_EXCL_LINE
5640             nodep->v3fatalSrc("Node needs sign change, but bad case: " << nodep);
5641             break;
5642         }
5643         UINFO(6, "   ReplaceWithUOrSVersion: " << nodep << " w/ " << newp << endl);
5644         nodep->replaceWith(newp);
5645         newp->dtypeFrom(nodep);
5646         VL_DO_DANGLING(pushDeletep(nodep), nodep);
5647         return newp;
5648     }
replaceWithDVersion(AstNodeBiop * nodep)5649     AstNodeBiop* replaceWithDVersion(AstNodeBiop* nodep) {
5650         // Given a signed/unsigned node type, create the opposite type
5651         // Return new node or nullptr if nothing
5652         if (nodep->doubleFlavor()) return nullptr;
5653         FileLine* const fl = nodep->fileline();
5654         AstNode* const lhsp = nodep->lhsp()->unlinkFrBack();
5655         AstNode* const rhsp = nodep->rhsp()->unlinkFrBack();
5656         AstNodeBiop* newp = nullptr;
5657         // No width change on output;...                // All below have bool or double outputs
5658         switch (nodep->type()) {
5659         case AstType::atAdd: newp = new AstAddD(fl, lhsp, rhsp); break;
5660         case AstType::atSub: newp = new AstSubD(fl, lhsp, rhsp); break;
5661         case AstType::atPow: newp = new AstPowD(fl, lhsp, rhsp); break;
5662         case AstType::atEq:
5663         case AstType::atEqCase: newp = new AstEqD(fl, lhsp, rhsp); break;
5664         case AstType::atNeq:
5665         case AstType::atNeqCase: newp = new AstNeqD(fl, lhsp, rhsp); break;
5666         case AstType::atGt:
5667         case AstType::atGtS: newp = new AstGtD(fl, lhsp, rhsp); break;
5668         case AstType::atGte:
5669         case AstType::atGteS: newp = new AstGteD(fl, lhsp, rhsp); break;
5670         case AstType::atLt:
5671         case AstType::atLtS: newp = new AstLtD(fl, lhsp, rhsp); break;
5672         case AstType::atLte:
5673         case AstType::atLteS: newp = new AstLteD(fl, lhsp, rhsp); break;
5674         case AstType::atDiv:
5675         case AstType::atDivS: newp = new AstDivD(fl, lhsp, rhsp); break;
5676         case AstType::atMul:
5677         case AstType::atMulS: newp = new AstMulD(fl, lhsp, rhsp); break;
5678         default:  // LCOV_EXCL_LINE
5679             nodep->v3fatalSrc("Node needs conversion to double, but bad case: " << nodep);
5680             break;
5681         }
5682         UINFO(6, "   ReplaceWithDVersion: " << nodep << " w/ " << newp << endl);
5683         nodep->replaceWith(newp);
5684         // No width change; the default created type (bool or double) is correct
5685         VL_DO_DANGLING(pushDeletep(nodep), nodep);
5686         return newp;
5687     }
replaceWithNVersion(AstNodeBiop * nodep)5688     AstNodeBiop* replaceWithNVersion(AstNodeBiop* nodep) {
5689         // Given a signed/unsigned node type, replace with string version
5690         // Return new node or nullptr if nothing
5691         if (nodep->stringFlavor()) return nullptr;
5692         FileLine* const fl = nodep->fileline();
5693         AstNode* const lhsp = nodep->lhsp()->unlinkFrBack();
5694         AstNode* const rhsp = nodep->rhsp()->unlinkFrBack();
5695         AstNodeBiop* newp = nullptr;
5696         // No width change on output;...                // All below have bool or double outputs
5697         switch (nodep->type()) {
5698         case AstType::atEq:
5699         case AstType::atEqCase: newp = new AstEqN(fl, lhsp, rhsp); break;
5700         case AstType::atNeq:
5701         case AstType::atNeqCase: newp = new AstNeqN(fl, lhsp, rhsp); break;
5702         case AstType::atGt:
5703         case AstType::atGtS: newp = new AstGtN(fl, lhsp, rhsp); break;
5704         case AstType::atGte:
5705         case AstType::atGteS: newp = new AstGteN(fl, lhsp, rhsp); break;
5706         case AstType::atLt:
5707         case AstType::atLtS: newp = new AstLtN(fl, lhsp, rhsp); break;
5708         case AstType::atLte:
5709         case AstType::atLteS: newp = new AstLteN(fl, lhsp, rhsp); break;
5710         default:  // LCOV_EXCL_LINE
5711             nodep->v3fatalSrc("Node needs conversion to string, but bad case: " << nodep);
5712             break;
5713         }
5714         UINFO(6, "   ReplaceWithNVersion: " << nodep << " w/ " << newp << endl);
5715         nodep->replaceWith(newp);
5716         // No width change; the default created type (bool or string) is correct
5717         VL_DO_DANGLING(pushDeletep(nodep), nodep);
5718         return newp;
5719     }
replaceWithDVersion(AstNodeUniop * nodep)5720     AstNodeUniop* replaceWithDVersion(AstNodeUniop* nodep) {
5721         // Given a signed/unsigned node type, create the opposite type
5722         // Return new node or nullptr if nothing
5723         if (nodep->doubleFlavor()) return nullptr;
5724         FileLine* const fl = nodep->fileline();
5725         AstNode* const lhsp = nodep->lhsp()->unlinkFrBack();
5726         AstNodeUniop* newp = nullptr;
5727         switch (nodep->type()) {
5728         case AstType::atNegate: newp = new AstNegateD(fl, lhsp); break;
5729         default:  // LCOV_EXCL_LINE
5730             nodep->v3fatalSrc("Node needs conversion to double, but bad case: " << nodep);
5731             break;
5732         }
5733         UINFO(6, "   ReplaceWithDVersion: " << nodep << " w/ " << newp << endl);
5734         nodep->replaceWith(newp);
5735         newp->dtypeFrom(nodep);
5736         VL_DO_DANGLING(pushDeletep(nodep), nodep);
5737         return newp;
5738     }
5739 
5740     //----------------------------------------------------------------------
5741     // METHODS - strings
5742 
replaceWithSFormat(AstMethodCall * nodep,const string & format)5743     void replaceWithSFormat(AstMethodCall* nodep, const string& format) {
5744         // For string.itoa and similar, replace with SFormatF
5745         const AstArg* argp = VN_CAST(nodep->pinsp(), Arg);
5746         if (!argp) {
5747             nodep->v3error("Argument needed for string." + nodep->prettyName() + " method");
5748             return;
5749         }
5750         AstNodeVarRef* const fromp = VN_AS(nodep->fromp()->unlinkFrBack(), VarRef);
5751         AstNode* const newp = new AstAssign(
5752             nodep->fileline(), fromp,
5753             new AstSFormatF(nodep->fileline(), format, false, argp->exprp()->unlinkFrBack()));
5754         fromp->access(VAccess::WRITE);
5755         nodep->replaceWith(newp);
5756         VL_DO_DANGLING(pushDeletep(nodep), nodep);
5757     }
5758 
5759     //----------------------------------------------------------------------
5760     // METHODS - data types
5761 
iterateEditMoveDTypep(AstNode * parentp,AstNodeDType * dtnodep)5762     AstNodeDType* iterateEditMoveDTypep(AstNode* parentp, AstNodeDType* dtnodep) {
5763         UASSERT_OBJ(dtnodep, parentp, "Caller should check for nullptr before computing dtype");
5764         // Iterate into a data type to resolve that type.
5765         // The data type may either:
5766         // 1. Be a child (typically getChildDTypep() returns it)
5767         //    DTypes at parse time get added as these to some node types
5768         //    such as AstVars.
5769         //    This function will move it to global scope (that is #2
5770         //    will now apply).
5771         // 2. Be under the Netlist and pointed to by an Ast member variable
5772         //    (typically refDTypep() or dtypep() returns it)
5773         //    so removing/changing a variable won't lose the dtype
5774 
5775         // Case #1 above applies?
5776         const bool child1 = (parentp->getChildDTypep() == dtnodep);
5777         const bool child2 = (parentp->getChild2DTypep() == dtnodep);
5778         if (child1 || child2) {
5779             UINFO(9, "iterateEditMoveDTypep child iterating " << dtnodep << endl);
5780             // Iterate, this might edit the dtypes which means dtnodep now lost
5781             VL_DO_DANGLING(userIterate(dtnodep, nullptr), dtnodep);
5782             // Figure out the new dtnodep, remained a child of parent so find it there
5783             dtnodep = child1 ? parentp->getChildDTypep() : parentp->getChild2DTypep();
5784             UASSERT_OBJ(dtnodep, parentp, "iterateEditMoveDTypep lost pointer to child");
5785             UASSERT_OBJ(dtnodep->didWidth(), parentp,
5786                         "iterateEditMoveDTypep didn't get width resolution of "
5787                             << dtnodep->prettyTypeName());
5788             // Move to under netlist
5789             UINFO(9, "iterateEditMoveDTypep child moving " << dtnodep << endl);
5790             dtnodep->unlinkFrBack();
5791             v3Global.rootp()->typeTablep()->addTypesp(dtnodep);
5792         }
5793         if (!dtnodep->didWidth()) {
5794             UINFO(9, "iterateEditMoveDTypep pointer iterating " << dtnodep << endl);
5795             // See notes in visit(AstBracketArrayDType*)
5796             UASSERT_OBJ(!VN_IS(dtnodep, BracketArrayDType), parentp,
5797                         "Brackets should have been iterated as children");
5798             userIterate(dtnodep, nullptr);
5799             UASSERT_OBJ(dtnodep->didWidth(), parentp,
5800                         "iterateEditMoveDTypep didn't get width resolution");
5801         }
5802         return dtnodep;
5803     }
5804 
dimensionValue(FileLine * fileline,AstNodeDType * nodep,AstAttrType attrType,int dim)5805     AstConst* dimensionValue(FileLine* fileline, AstNodeDType* nodep, AstAttrType attrType,
5806                              int dim) {
5807         // Return the dimension value for the specified attribute and constant dimension
5808         AstNodeDType* dtypep = nodep->skipRefp();
5809         VNumRange declRange;  // ranged() set false
5810         for (int i = 1; i <= dim; ++i) {
5811             // UINFO(9, "   dim at "<<dim<<"  "<<dtypep<<endl);
5812             declRange = VNumRange();  // ranged() set false
5813             if (const AstNodeArrayDType* const adtypep = VN_CAST(dtypep, NodeArrayDType)) {
5814                 declRange = adtypep->declRange();
5815                 if (i < dim) dtypep = adtypep->subDTypep()->skipRefp();
5816                 continue;
5817             } else if (const AstNodeUOrStructDType* const adtypep
5818                        = VN_CAST(dtypep, NodeUOrStructDType)) {
5819                 declRange = adtypep->declRange();
5820                 break;  // Sub elements don't look like arrays and can't iterate into
5821             } else if (const AstBasicDType* const adtypep = VN_CAST(dtypep, BasicDType)) {
5822                 if (adtypep->isRanged()) declRange = adtypep->declRange();
5823                 break;
5824             }
5825             break;  // LCOV_EXCL_LINE
5826         }
5827         AstConst* valp = nullptr;  // If nullptr, construct from val
5828         int val = 0;
5829         switch (attrType) {
5830         case AstAttrType::DIM_BITS: {
5831             int bits = 1;
5832             while (dtypep) {
5833                 // UINFO(9, "   bits at "<<bits<<"  "<<dtypep<<endl);
5834                 if (const AstNodeArrayDType* const adtypep = VN_CAST(dtypep, NodeArrayDType)) {
5835                     bits *= adtypep->declRange().elements();
5836                     dtypep = adtypep->subDTypep()->skipRefp();
5837                     continue;
5838                 } else if (const AstNodeUOrStructDType* const adtypep
5839                            = VN_CAST(dtypep, NodeUOrStructDType)) {
5840                     bits *= adtypep->width();
5841                     break;
5842                 } else if (const AstBasicDType* const adtypep = VN_CAST(dtypep, BasicDType)) {
5843                     bits *= adtypep->width();
5844                     break;
5845                 }
5846                 break;
5847             }
5848             if (dim == 0) {
5849                 val = 0;
5850             } else if (dim == 1 && !declRange.ranged()
5851                        && bits == 1) {  // $bits should be sane for non-arrays
5852                 val = nodep->width();
5853             } else {
5854                 val = bits;
5855             }
5856             break;  // LCOV_EXCL_LINE
5857         }
5858         case AstAttrType::DIM_HIGH: val = !declRange.ranged() ? 0 : declRange.hi(); break;
5859         case AstAttrType::DIM_LEFT: val = !declRange.ranged() ? 0 : declRange.left(); break;
5860         case AstAttrType::DIM_LOW: val = !declRange.ranged() ? 0 : declRange.lo(); break;
5861         case AstAttrType::DIM_RIGHT: val = !declRange.ranged() ? 0 : declRange.right(); break;
5862         case AstAttrType::DIM_INCREMENT:
5863             val = (declRange.ranged() && declRange.littleEndian()) ? -1 : 1;
5864             break;
5865         case AstAttrType::DIM_SIZE: val = !declRange.ranged() ? 0 : declRange.elements(); break;
5866         default: nodep->v3fatalSrc("Missing DIM ATTR type case"); break;
5867         }
5868         if (!valp) valp = new AstConst(fileline, AstConst::Signed32(), val);
5869         UINFO(9, " $dimension " << attrType.ascii() << "(" << cvtToHex(dtypep) << "," << dim
5870                                 << ")=" << valp << endl);
5871         return valp;
5872     }
dimensionVarp(AstNodeDType * nodep,AstAttrType attrType,uint32_t msbdim)5873     AstVar* dimensionVarp(AstNodeDType* nodep, AstAttrType attrType, uint32_t msbdim) {
5874         // Return a variable table which has specified dimension properties for this variable
5875         const auto pos = m_tableMap.find(std::make_pair(nodep, attrType));
5876         if (pos != m_tableMap.end()) return pos->second;
5877         AstNodeArrayDType* const vardtypep
5878             = new AstUnpackArrayDType(nodep->fileline(), nodep->findSigned32DType(),
5879                                       new AstRange(nodep->fileline(), msbdim, 0));
5880         AstInitArray* const initp = new AstInitArray(nodep->fileline(), vardtypep, nullptr);
5881         v3Global.rootp()->typeTablep()->addTypesp(vardtypep);
5882         AstVar* const varp = new AstVar(nodep->fileline(), AstVarType::MODULETEMP,
5883                                         "__Vdimtab_" + VString::downcase(attrType.ascii())
5884                                             + cvtToStr(m_dtTables++),
5885                                         vardtypep);
5886         varp->isConst(true);
5887         varp->isStatic(true);
5888         varp->valuep(initp);
5889         // Add to root, as don't know module we are in, and aids later structure sharing
5890         v3Global.rootp()->dollarUnitPkgAddp()->addStmtp(varp);
5891         // Element 0 is a non-index and has speced values
5892         initp->addValuep(dimensionValue(nodep->fileline(), nodep, attrType, 0));
5893         for (unsigned i = 1; i < msbdim + 1; ++i) {
5894             initp->addValuep(dimensionValue(nodep->fileline(), nodep, attrType, i));
5895         }
5896         userIterate(varp, nullptr);  // May have already done $unit so must do this var
5897         m_tableMap.emplace(std::make_pair(nodep, attrType), varp);
5898         return varp;
5899     }
enumMaxValue(const AstNode * errNodep,const AstEnumDType * adtypep)5900     uint64_t enumMaxValue(const AstNode* errNodep, const AstEnumDType* adtypep) {
5901         // Most enums unless overridden are 32 bits, so we size array
5902         // based on max enum value used.
5903         // Ideally we would have a fast algorithm when a number is
5904         // of small width and complete and so can use an array, and
5905         // a map for when the value is many bits and sparse.
5906         uint64_t maxval = 0;
5907         for (const AstEnumItem* itemp = adtypep->itemsp(); itemp;
5908              itemp = VN_AS(itemp->nextp(), EnumItem)) {
5909             const AstConst* const vconstp = VN_AS(itemp->valuep(), Const);
5910             UASSERT_OBJ(vconstp, errNodep, "Enum item without constified value");
5911             if (vconstp->toUQuad() >= maxval) maxval = vconstp->toUQuad();
5912         }
5913         if (adtypep->itemsp()->width() > 64 || maxval >= (1 << 16)) {
5914             errNodep->v3warn(E_UNSUPPORTED,
5915                              "Unsupported: enum next/prev method on enum with > 10 bits");
5916             return 0;
5917         }
5918         return maxval;
5919     }
enumVarp(AstEnumDType * nodep,AstAttrType attrType,uint32_t msbdim)5920     AstVar* enumVarp(AstEnumDType* nodep, AstAttrType attrType, uint32_t msbdim) {
5921         // Return a variable table which has specified dimension properties for this variable
5922         const auto pos = m_tableMap.find(std::make_pair(nodep, attrType));
5923         if (pos != m_tableMap.end()) return pos->second;
5924         UINFO(9, "Construct Venumtab attr=" << attrType.ascii() << " max=" << msbdim << " for "
5925                                             << nodep << endl);
5926         AstNodeDType* basep;
5927         if (attrType == AstAttrType::ENUM_NAME) {
5928             basep = nodep->findStringDType();
5929         } else if (attrType == AstAttrType::ENUM_VALID) {
5930             // TODO in theory we could bit-pack the bits in the table, but
5931             // would require additional operations to extract, so only
5932             // would be worth it for larger tables which perhaps could be
5933             // better handled with equation generation?
5934             basep = nodep->findBitDType();
5935         } else {
5936             basep = nodep->dtypep();
5937         }
5938         AstNodeArrayDType* const vardtypep = new AstUnpackArrayDType(
5939             nodep->fileline(), basep, new AstRange(nodep->fileline(), msbdim, 0));
5940         AstInitArray* const initp = new AstInitArray(nodep->fileline(), vardtypep, nullptr);
5941         v3Global.rootp()->typeTablep()->addTypesp(vardtypep);
5942         AstVar* const varp = new AstVar(nodep->fileline(), AstVarType::MODULETEMP,
5943                                         "__Venumtab_" + VString::downcase(attrType.ascii())
5944                                             + cvtToStr(m_dtTables++),
5945                                         vardtypep);
5946         varp->isConst(true);
5947         varp->isStatic(true);
5948         varp->valuep(initp);
5949         // Add to root, as don't know module we are in, and aids later structure sharing
5950         v3Global.rootp()->dollarUnitPkgAddp()->addStmtp(varp);
5951 
5952         // Default for all unspecified values
5953         if (attrType == AstAttrType::ENUM_NAME) {
5954             initp->defaultp(new AstConst(nodep->fileline(), AstConst::String(), ""));
5955         } else if (attrType == AstAttrType::ENUM_NEXT || attrType == AstAttrType::ENUM_PREV) {
5956             initp->defaultp(new AstConst(nodep->fileline(), V3Number(nodep, nodep->width(), 0)));
5957         } else if (attrType == AstAttrType::ENUM_VALID) {
5958             initp->defaultp(new AstConst{nodep->fileline(), AstConst::BitFalse{}});
5959         } else {
5960             nodep->v3fatalSrc("Bad case");
5961         }
5962 
5963         // Find valid values and populate
5964         UASSERT_OBJ(nodep->itemsp(), nodep, "enum without items");
5965         std::vector<AstNode*> values;
5966         values.resize(msbdim + 1);
5967         for (unsigned i = 0; i < (msbdim + 1); ++i) values[i] = nullptr;
5968         {
5969             AstEnumItem* const firstp = nodep->itemsp();
5970             const AstEnumItem* prevp = firstp;  // Prev must start with last item
5971             while (prevp->nextp()) prevp = VN_AS(prevp->nextp(), EnumItem);
5972             for (AstEnumItem* itemp = firstp; itemp;) {
5973                 AstEnumItem* const nextp = VN_AS(itemp->nextp(), EnumItem);
5974                 const AstConst* const vconstp = VN_AS(itemp->valuep(), Const);
5975                 UASSERT_OBJ(vconstp, nodep, "Enum item without constified value");
5976                 const uint32_t i = vconstp->toUInt();
5977                 if (attrType == AstAttrType::ENUM_NAME) {
5978                     values[i] = new AstConst(nodep->fileline(), AstConst::String(), itemp->name());
5979                 } else if (attrType == AstAttrType::ENUM_NEXT) {
5980                     values[i] = (nextp ? nextp : firstp)->valuep()->cloneTree(false);  // A const
5981                 } else if (attrType == AstAttrType::ENUM_PREV) {
5982                     values[i] = prevp->valuep()->cloneTree(false);  // A const
5983                 } else if (attrType == AstAttrType::ENUM_VALID) {
5984                     values[i] = new AstConst(nodep->fileline(), AstConst::BitTrue{});
5985                 } else {
5986                     nodep->v3fatalSrc("Bad case");
5987                 }
5988                 prevp = itemp;
5989                 itemp = nextp;
5990             }
5991         }
5992         // Add all specified values to table
5993         for (unsigned i = 0; i < (msbdim + 1); ++i) {
5994             if (values[i]) initp->addIndexValuep(i, values[i]);
5995         }
5996         userIterate(varp, nullptr);  // May have already done $unit so must do this var
5997         m_tableMap.emplace(std::make_pair(nodep, attrType), varp);
5998         return varp;
5999     }
6000 
patVectorMap(AstPattern * nodep,const VNumRange & range)6001     PatVecMap patVectorMap(AstPattern* nodep, const VNumRange& range) {
6002         PatVecMap patmap;
6003         int element = range.left();
6004         for (AstPatMember* patp = VN_AS(nodep->itemsp(), PatMember); patp;
6005              patp = VN_AS(patp->nextp(), PatMember)) {
6006             if (patp->keyp()) {
6007                 if (const AstConst* const constp = VN_CAST(patp->keyp(), Const)) {
6008                     element = constp->toSInt();
6009                 } else {
6010                     patp->keyp()->v3error("Assignment pattern key not supported/understood: "
6011                                           << patp->keyp()->prettyTypeName());
6012                 }
6013             }
6014             if (patmap.find(element) != patmap.end()) {
6015                 patp->v3error("Assignment pattern key used multiple times: " << element);
6016             } else {
6017                 patmap.emplace(element, patp);
6018             }
6019             element += range.leftToRightInc();
6020         }
6021         return patmap;
6022     }
6023 
makeOpenArrayShell(AstNodeFTaskRef * nodep)6024     void makeOpenArrayShell(AstNodeFTaskRef* nodep) {
6025         UINFO(4, "Replicate openarray function " << nodep->taskp() << endl);
6026         AstNodeFTask* const oldTaskp = nodep->taskp();
6027         oldTaskp->dpiOpenParentInc();
6028         UASSERT_OBJ(!oldTaskp->dpiOpenChild(), oldTaskp,
6029                     "DPI task should be parent or child, not both");
6030         AstNodeFTask* const newTaskp = oldTaskp->cloneTree(false);
6031         newTaskp->dpiOpenChild(true);
6032         newTaskp->dpiOpenParentClear();
6033         newTaskp->name(newTaskp->name() + "__Vdpioc" + cvtToStr(oldTaskp->dpiOpenParent()));
6034         oldTaskp->addNextHere(newTaskp);
6035         // Relink reference to new function
6036         nodep->taskp(newTaskp);
6037         nodep->name(nodep->taskp()->name());
6038         // Replace open array arguments with the callee's task
6039         const V3TaskConnects tconnects = V3Task::taskConnects(nodep, nodep->taskp()->stmtsp());
6040         for (const auto& tconnect : tconnects) {
6041             AstVar* const portp = tconnect.first;
6042             const AstArg* const argp = tconnect.second;
6043             const AstNode* const pinp = argp->exprp();
6044             if (!pinp) continue;  // Argument error we'll find later
6045             if (hasOpenArrayIterateDType(portp->dtypep())) portp->dtypep(pinp->dtypep());
6046         }
6047     }
6048 
markHasOpenArray(AstNodeFTask * nodep)6049     bool markHasOpenArray(AstNodeFTask* nodep) {
6050         bool hasOpen = false;
6051         for (AstNode* stmtp = nodep->stmtsp(); stmtp; stmtp = stmtp->nextp()) {
6052             if (AstVar* const portp = VN_CAST(stmtp, Var)) {
6053                 if (portp->isDpiOpenArray() || hasOpenArrayIterateDType(portp->dtypep())) {
6054                     portp->isDpiOpenArray(true);
6055                     hasOpen = true;
6056                 }
6057             }
6058         }
6059         return hasOpen;
6060     }
hasOpenArrayIterateDType(AstNodeDType * nodep)6061     bool hasOpenArrayIterateDType(AstNodeDType* nodep) {
6062         // Return true iff this datatype or child has an openarray
6063         if (VN_IS(nodep, UnsizedArrayDType)) return true;
6064         if (nodep->subDTypep()) return hasOpenArrayIterateDType(nodep->subDTypep()->skipRefp());
6065         return false;
6066     }
6067 
6068     //----------------------------------------------------------------------
6069     // METHODS - casting
computeCastable(const AstNodeDType * toDtp,const AstNodeDType * fromDtp,const AstNode * fromConstp)6070     static Castable computeCastable(const AstNodeDType* toDtp, const AstNodeDType* fromDtp,
6071                                     const AstNode* fromConstp) {
6072         const auto castable = computeCastableImp(toDtp, fromDtp, fromConstp);
6073         UINFO(9, "  castable=" << castable << "  for " << toDtp << endl);
6074         UINFO(9, "     =?= " << fromDtp << endl);
6075         UINFO(9, "     const= " << fromConstp << endl);
6076         return castable;
6077     }
computeCastableImp(const AstNodeDType * toDtp,const AstNodeDType * fromDtp,const AstNode * fromConstp)6078     static Castable computeCastableImp(const AstNodeDType* toDtp, const AstNodeDType* fromDtp,
6079                                        const AstNode* fromConstp) {
6080         const Castable castable = UNSUPPORTED;
6081         toDtp = toDtp->skipRefToEnump();
6082         fromDtp = fromDtp->skipRefToEnump();
6083         if (toDtp == fromDtp) return COMPATIBLE;
6084         const AstNodeDType* fromBaseDtp = fromDtp;
6085         while (const AstPackArrayDType* const packp = VN_CAST(fromBaseDtp, PackArrayDType)) {
6086             fromBaseDtp = packp->subDTypep();
6087             while (const AstRefDType* const refp = VN_CAST(fromBaseDtp, RefDType)) {
6088                 fromBaseDtp = refp->refDTypep();
6089             }
6090         }
6091         const bool fromNumericable = VN_IS(fromBaseDtp, BasicDType)
6092                                      || VN_IS(fromBaseDtp, EnumDType)
6093                                      || VN_IS(fromBaseDtp, NodeUOrStructDType);
6094         // UNSUP unpacked struct/unions (treated like BasicDType)
6095         if (VN_IS(toDtp, BasicDType) || VN_IS(toDtp, NodeUOrStructDType)) {
6096             if (fromNumericable) return COMPATIBLE;
6097         } else if (VN_IS(toDtp, EnumDType)) {
6098             if (fromNumericable) return DYNAMIC_ENUM;
6099         } else if (VN_IS(toDtp, ClassRefDType) && VN_IS(fromConstp, Const)) {
6100             if (VN_IS(fromConstp, Const) && VN_AS(fromConstp, Const)->num().isNull())
6101                 return COMPATIBLE;
6102         } else if (VN_IS(toDtp, ClassRefDType) && VN_IS(fromDtp, ClassRefDType)) {
6103             const auto toClassp = VN_AS(toDtp, ClassRefDType)->classp();
6104             const auto fromClassp = VN_AS(fromDtp, ClassRefDType)->classp();
6105             const bool downcast = AstClass::isClassExtendedFrom(toClassp, fromClassp);
6106             const bool upcast = AstClass::isClassExtendedFrom(fromClassp, toClassp);
6107             if (upcast) {
6108                 return COMPATIBLE;
6109             } else if (downcast) {
6110                 return DYNAMIC_CLASS;
6111             } else {
6112                 return INCOMPATIBLE;
6113             }
6114         }
6115         return castable;
6116     }
6117 
6118     //----------------------------------------------------------------------
6119     // METHODS - special type detection
assertAtStatement(AstNode * nodep)6120     void assertAtStatement(AstNode* nodep) {
6121         if (VL_UNCOVERABLE(m_vup && !m_vup->selfDtm())) {
6122             UINFO(1, "-: " << m_vup << endl);
6123             nodep->v3fatalSrc("No dtype expected at statement " << nodep->prettyTypeName());
6124         }
6125     }
checkConstantOrReplace(AstNode * nodep,const string & message)6126     void checkConstantOrReplace(AstNode* nodep, const string& message) {
6127         // See also V3WidthSel::checkConstantOrReplace
6128         // Note can't call V3Const::constifyParam(nodep) here, as constify may change nodep on us!
6129         if (!VN_IS(nodep, Const)) {
6130             nodep->v3error(message);
6131             nodep->replaceWith(new AstConst(nodep->fileline(), AstConst::Unsized32(), 1));
6132             VL_DO_DANGLING(pushDeletep(nodep), nodep);
6133         }
6134     }
nodeForUnsizedWarning(AstNode * nodep)6135     AstNode* nodeForUnsizedWarning(AstNode* nodep) {
6136         // Return a nodep to use for unsized warnings, reporting on child if can
6137         if (nodep->op1p() && nodep->op1p()->dtypep() && !nodep->op1p()->dtypep()->widthSized()) {
6138             return nodep->op1p();
6139         } else if (nodep->op2p() && nodep->op2p()->dtypep()
6140                    && !nodep->op2p()->dtypep()->widthSized()) {
6141             return nodep->op2p();
6142         }
6143         return nodep;  // By default return this
6144     }
checkRefToTypedefRecurse(AstNode * nodep,AstTypedef * typedefp)6145     AstRefDType* checkRefToTypedefRecurse(AstNode* nodep, AstTypedef* typedefp) {
6146         // Recurse all children looking for self reference
6147         // This avoids iterateEditMoveDTypep going into a hard to resolve loop
6148         // Only call once for any given typedef, or will become O(n^2)
6149         if (VL_LIKELY(!nodep)) return nullptr;
6150         if (auto* const refp = VN_CAST(nodep, RefDType)) {
6151             if (refp->typedefp() == typedefp) return refp;
6152         }
6153         if (auto* const refp = checkRefToTypedefRecurse(nodep->op1p(), typedefp)) return refp;
6154         if (auto* const refp = checkRefToTypedefRecurse(nodep->op2p(), typedefp)) return refp;
6155         if (auto* const refp = checkRefToTypedefRecurse(nodep->op3p(), typedefp)) return refp;
6156         if (auto* const refp = checkRefToTypedefRecurse(nodep->op4p(), typedefp)) return refp;
6157         return nullptr;
6158     }
6159 
6160     //----------------------------------------------------------------------
6161     // METHODS - special iterators
6162     // These functions save/restore the AstNUser information so it can pass to child nodes.
6163 
userIterateSubtreeReturnEdits(AstNode * nodep,WidthVP * vup)6164     AstNode* userIterateSubtreeReturnEdits(AstNode* nodep, WidthVP* vup) {
6165         if (!nodep) return nullptr;
6166         AstNode* ret;
6167         {
6168             VL_RESTORER(m_vup);
6169             m_vup = vup;
6170             ret = iterateSubtreeReturnEdits(nodep);
6171         }
6172         return ret;
6173     }
userIterate(AstNode * nodep,WidthVP * vup)6174     void userIterate(AstNode* nodep, WidthVP* vup) {
6175         if (!nodep) return;
6176         {
6177             VL_RESTORER(m_vup);
6178             m_vup = vup;
6179             iterate(nodep);
6180         }
6181     }
userIterateAndNext(AstNode * nodep,WidthVP * vup)6182     void userIterateAndNext(AstNode* nodep, WidthVP* vup) {
6183         if (!nodep) return;
6184         if (nodep->didWidth()) return;  // Avoid iterating list we have already iterated
6185         {
6186             VL_RESTORER(m_vup);
6187             m_vup = vup;
6188             iterateAndNextNull(nodep);
6189         }
6190     }
userIterateChildren(AstNode * nodep,WidthVP * vup)6191     void userIterateChildren(AstNode* nodep, WidthVP* vup) {
6192         if (!nodep) return;
6193         {
6194             VL_RESTORER(m_vup);
6195             m_vup = vup;
6196             iterateChildren(nodep);
6197         }
6198     }
userIterateChildrenBackwards(AstNode * nodep,WidthVP * vup)6199     void userIterateChildrenBackwards(AstNode* nodep, WidthVP* vup) {
6200         if (!nodep) return;
6201         {
6202             VL_RESTORER(m_vup);
6203             m_vup = vup;
6204             iterateChildrenBackwards(nodep);
6205         }
6206     }
6207 
6208 public:
6209     // CONSTRUCTORS
WidthVisitor(bool paramsOnly,bool doGenerate)6210     WidthVisitor(bool paramsOnly,  // [in] TRUE if we are considering parameters only.
6211                  bool doGenerate)  // [in] TRUE if we are inside a generate statement and
6212         //                           // don't wish to trigger errors
6213         : m_paramsOnly{paramsOnly}
6214         , m_doGenerate{doGenerate} {}
mainAcceptEdit(AstNode * nodep)6215     AstNode* mainAcceptEdit(AstNode* nodep) {
6216         return userIterateSubtreeReturnEdits(nodep, WidthVP(SELF, BOTH).p());
6217     }
6218     virtual ~WidthVisitor() override = default;
6219 };
6220 
6221 //######################################################################
6222 // Width class functions
6223 
debug()6224 int V3Width::debug() {
6225     static int level = -1;
6226     if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
6227     return level;
6228 }
6229 
width(AstNetlist * nodep)6230 void V3Width::width(AstNetlist* nodep) {
6231     UINFO(2, __FUNCTION__ << ": " << endl);
6232     {
6233         // We should do it in bottom-up module order, but it works in any order.
6234         const WidthClearVisitor cvisitor{nodep};
6235         WidthVisitor visitor{false, false};
6236         (void)visitor.mainAcceptEdit(nodep);
6237         WidthRemoveVisitor rvisitor;
6238         (void)rvisitor.mainAcceptEdit(nodep);
6239     }  // Destruct before checking
6240     V3Global::dumpCheckGlobalTree("width", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
6241 }
6242 
6243 //! Single node parameter propagation
6244 //! Smaller step... Only do a single node for parameter propagation
widthParamsEdit(AstNode * nodep)6245 AstNode* V3Width::widthParamsEdit(AstNode* nodep) {
6246     UINFO(4, __FUNCTION__ << ": " << nodep << endl);
6247     // We should do it in bottom-up module order, but it works in any order.
6248     WidthVisitor visitor{true, false};
6249     nodep = visitor.mainAcceptEdit(nodep);
6250     // No WidthRemoveVisitor, as don't want to drop $signed etc inside gen blocks
6251     return nodep;
6252 }
6253 
6254 //! Single node parameter propagation for generate blocks.
6255 //! Smaller step... Only do a single node for parameter propagation
6256 //! If we are inside a generated "if", "case" or "for", we don't want to
6257 //! trigger warnings when we deal with the width. It is possible that
6258 //! these are spurious, existing within sub-expressions that will not
6259 //! actually be generated. Since such occurrences, must be constant, in
6260 //! order to be something a generate block can depend on, we can wait until
6261 //! later to do the width check.
6262 //! @return  Pointer to the edited node.
widthGenerateParamsEdit(AstNode * nodep)6263 AstNode* V3Width::widthGenerateParamsEdit(
6264     AstNode* nodep) {  //!< [in] AST whose parameters widths are to be analysed.
6265     UINFO(4, __FUNCTION__ << ": " << nodep << endl);
6266     // We should do it in bottom-up module order, but it works in any order.
6267     WidthVisitor visitor{true, true};
6268     nodep = visitor.mainAcceptEdit(nodep);
6269     // No WidthRemoveVisitor, as don't want to drop $signed etc inside gen blocks
6270     return nodep;
6271 }
6272 
widthCommit(AstNetlist * nodep)6273 void V3Width::widthCommit(AstNetlist* nodep) {
6274     UINFO(2, __FUNCTION__ << ": " << endl);
6275     { WidthCommitVisitor{nodep}; }  // Destruct before checking
6276     V3Global::dumpCheckGlobalTree("widthcommit", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 6);
6277 }
6278