Branch data Line data Source code
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 : : // This program is free software; you can redistribute it and/or modify it
10 : : // under the terms of either the GNU Lesser General Public License Version 3
11 : : // or the Perl Artistic License Version 2.0.
12 : : // SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
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 "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT
67 : :
68 : : #include "V3Width.h"
69 : :
70 : : #include "V3Ast.h"
71 : : #include "V3Begin.h"
72 : : #include "V3Const.h"
73 : : #include "V3Error.h"
74 : : #include "V3Global.h"
75 : : #include "V3LinkDotIfaceCapture.h"
76 : : #include "V3LinkLValue.h"
77 : : #include "V3MemberMap.h"
78 : : #include "V3Number.h"
79 : : #include "V3Randomize.h"
80 : : #include "V3String.h"
81 : : #include "V3Task.h"
82 : : #include "V3UniqueNames.h"
83 : : #include "V3WidthCommit.h"
84 : :
85 : : // More code; this file was getting too large; see actions there
86 : : #define VERILATOR_V3WIDTH_CPP_
87 : : #include "V3WidthRemove.h"
88 : :
89 : : VL_DEFINE_DEBUG_FUNCTIONS;
90 : :
91 : : //######################################################################
92 : :
93 : : enum Stage : uint8_t {
94 : : PRELIM = 1,
95 : : FINAL = 2,
96 : : BOTH = 3
97 : : }; // Numbers are a bitmask <0>=prelim, <1>=final
98 : : std::ostream& operator<<(std::ostream& str, const Stage& rhs) {
99 : : return str << ("-PFB"[static_cast<int>(rhs)]);
100 : : }
101 : :
102 : : enum Determ : uint8_t {
103 : : SELF, // Self-determined
104 : : CONTEXT_DET, // Context-determined
105 : : ASSIGN // Assignment-like where sign comes from RHS only
106 : : };
107 : : std::ostream& operator<<(std::ostream& str, const Determ& rhs) {
108 : : static const char* const s_det[] = {"SELF", "CNTX", "ASSN"};
109 : : return str << s_det[rhs];
110 : : }
111 : :
112 : : #define v3widthWarn(lhs, rhs, msg) \
113 : : v3warnCode(((lhs) < (rhs) ? V3ErrorCode::WIDTHTRUNC \
114 : : : (lhs) > (rhs) ? V3ErrorCode::WIDTHEXPAND \
115 : : : V3ErrorCode::WIDTH), \
116 : : msg)
117 : :
118 : : //######################################################################
119 : : // Width state, as a visitor of each AstNode
120 : :
121 : : class WidthVP final {
122 : : // Parameters to pass down hierarchy with visit functions.
123 : : AstNodeDType* const m_dtypep; // Parent's data type to resolve to
124 : : const Stage m_stage; // If true, report errors
125 : : public:
126 : : WidthVP(AstNodeDType* dtypep, Stage stage)
127 : : : m_dtypep{dtypep}
128 : : , m_stage{stage} {
129 : : // Prelim doesn't look at assignments, so shouldn't need a dtype,
130 : : // however AstPattern uses them
131 : : }
132 : : WidthVP(Determ determ, Stage stage)
133 : : : m_dtypep{nullptr}
134 : : , m_stage{stage} {
135 : : if (determ != SELF && stage != PRELIM)
136 : : v3fatalSrc("Context-determined width request only allowed as prelim step");
137 : : }
138 : : WidthVP* p() { return this; }
139 : : bool selfDtm() const { return m_dtypep == nullptr; }
140 : : AstNodeDType* dtypep() const {
141 : : // Detect where overrideDType is probably the intended call
142 : : UASSERT(m_dtypep, "Width dtype request on self-determined or preliminary VUP");
143 : : return m_dtypep;
144 : : }
145 : : AstNodeDType* dtypeNullp() const { return m_dtypep; }
146 : : AstNodeDType* dtypeNullSkipRefp() const {
147 : : AstNodeDType* dtp = dtypeNullp();
148 : : if (dtp) dtp = dtp->skipRefp();
149 : : return dtp;
150 : : }
151 : : AstNodeDType* dtypeOverridep(AstNodeDType* defaultp) const {
152 : : UASSERT(m_stage != PRELIM, "Parent dtype should be a final-stage action");
153 : : return m_dtypep ? m_dtypep : defaultp;
154 : : }
155 : : int width() const {
156 : : UASSERT(m_dtypep, "Width request on self-determined or preliminary VUP");
157 : : return m_dtypep->width();
158 : : }
159 : : int widthMin() const {
160 : : UASSERT(m_dtypep, "Width request on self-determined or preliminary VUP");
161 : : return m_dtypep->widthMin();
162 : : }
163 : : bool prelim() const { return m_stage & PRELIM; }
164 : : bool final() const { return m_stage & FINAL; }
165 : : void dump(std::ostream& str) const {
166 : : if (!m_dtypep) {
167 : : str << " VUP(s=" << m_stage << ",self)";
168 : : } else {
169 : : str << " VUP(s=" << m_stage << ",dt=" << cvtToHex(dtypep());
170 : : dtypep()->dumpSmall(str);
171 : : str << ")";
172 : : }
173 : : }
174 : : };
175 : : std::ostream& operator<<(std::ostream& str, const WidthVP* vup) {
176 : : if (vup) vup->dump(str);
177 : : return str;
178 : : }
179 : :
180 : : //######################################################################
181 : :
182 : : class WidthClearVisitor final {
183 : : // Rather than a VNVisitor, can just quickly touch every node
184 : : void clearWidthRecurse(AstNode* nodep) {
185 : : for (; nodep; nodep = nodep->nextp()) {
186 : : nodep->didWidth(false);
187 : : if (AstNode* const refp = nodep->op1p()) clearWidthRecurse(refp);
188 : : if (AstNode* const refp = nodep->op2p()) clearWidthRecurse(refp);
189 : : if (AstNode* const refp = nodep->op3p()) clearWidthRecurse(refp);
190 : : if (AstNode* const refp = nodep->op4p()) clearWidthRecurse(refp);
191 : : }
192 : : }
193 : :
194 : : public:
195 : : // CONSTRUCTORS
196 : : explicit WidthClearVisitor(AstNetlist* nodep) { clearWidthRecurse(nodep); }
197 : : virtual ~WidthClearVisitor() = default;
198 : : };
199 : :
200 : : //######################################################################
201 : :
202 : : #define accept in_WidthVisitor_use_AstNode_iterate_instead_of_AstNode_accept
203 : :
204 : : //######################################################################
205 : :
206 : : class WidthVisitor final : public VNVisitor {
207 : : // TYPES
208 : : using TableMap = std::map<std::pair<const AstNodeDType*, VAttrType>, AstVar*>;
209 : : using PatVecMap = std::map<int, AstPatMember*>;
210 : : using DTypeMap = std::map<const std::string, AstPatMember*>;
211 : :
212 : : // STATE
213 : : V3UniqueNames m_insideTempNames; // For generating unique temporary variable names for
214 : : // `inside` expressions
215 : : VMemberMap m_memberMap; // Member names cached for fast lookup
216 : : V3TaskConnectState m_taskConnectState; // State to cache V3Task::taskConnects
217 : : WidthVP* m_vup = nullptr; // Current node state
218 : : bool m_underFork = false; // Visiting under a fork
219 : : bool m_underSExpr = false; // Visiting under a sequence expression
220 : : bool m_underPackedArray = false; // Visiting under a AstPackArrayDType
221 : : bool m_underMemberSel = false; // Viting under a MemberSel
222 : : bool m_hasNamedType = false; // Packed array is defined using named type
223 : : AstNode* m_seqUnsupp = nullptr; // Property has unsupported node
224 : : bool m_hasSExpr = false; // Property has a sequence expression
225 : : const AstCell* m_cellp = nullptr; // Current cell for arrayed instantiations
226 : : const AstEnumItem* m_enumItemp = nullptr; // Current enum item
227 : : AstNodeFTask* m_ftaskp = nullptr; // Current function/task
228 : : AstNodeModule* m_modep = nullptr; // Current module
229 : : const AstConstraint* m_constraintp = nullptr; // Current constraint
230 : : AstNodeProcedure* m_procedurep = nullptr; // Current final/always
231 : : const AstWith* m_withp = nullptr; // Current 'with' statement
232 : : const AstFunc* m_funcp = nullptr; // Current function
233 : : const AstAttrOf* m_attrp = nullptr; // Current attribute
234 : : const AstNodeExpr* m_randomizeFromp = nullptr; // Current randomize method call fromp
235 : : const bool m_paramsOnly; // Computing parameter value; limit operation
236 : : const bool m_doGenerate; // Do errors later inside generate statement
237 : : bool m_streamConcat = false; // True if visiting arguments of stream concatenation
238 : : int m_dtTables = 0; // Number of created data type tables
239 : : TableMap m_tableMap; // Created tables so can remove duplicates
240 : : std::map<const AstNodeDType*, AstQueueDType*>
241 : : m_queueDTypeIndexed; // Queues with given index type
242 : : std::map<const AstNode*, const AstClass*>
243 : : m_containingClassp; // Containing class cache for containingClass() function
244 : : std::unordered_set<AstVar*> m_aliasedVars; // Variables referenced in alias
245 : : std::unordered_set<const AstVar*> m_curModVars; // Variables declared in current module
246 : :
247 : : static constexpr int ENUM_LOOKUP_BITS = 16; // Maximum # bits to make enum lookup table
248 : :
249 : : // ENUMS
250 : : enum ExtendRule : uint8_t {
251 : : EXTEND_EXP, // Extend if expect sign and node signed, e.g. node=y in ADD(x,y), "x + y"
252 : : EXTEND_ZERO, // Extend with zeros. e.g. node=y in EQ(x,y), "x == y"
253 : : EXTEND_LHS, // Extend with sign if node signed. e.g. node=y in ASSIGN(y,x), "x = y"
254 : : EXTEND_OFF // No extension
255 : : };
256 : :
257 : : int widthUnpacked(const AstNodeDType* const dtypep) {
258 : : if (const AstUnpackArrayDType* const arrDtypep = VN_CAST(dtypep, UnpackArrayDType)) {
259 : : return arrDtypep->subDTypep()->width() * arrDtypep->arrayUnpackedElements();
260 : : }
261 : : return dtypep->width();
262 : : }
263 : :
264 : : static void packIfUnpacked(AstNodeExpr* const nodep) {
265 : : if (AstUnpackArrayDType* const unpackDTypep = VN_CAST(nodep->dtypep(), UnpackArrayDType)) {
266 : : const int elementsNum = unpackDTypep->arrayUnpackedElements();
267 : : const int unpackMinBits = elementsNum * unpackDTypep->subDTypep()->widthMin();
268 : : const int unpackBits = elementsNum * unpackDTypep->subDTypep()->width();
269 : : VNRelinker relinker;
270 : : nodep->unlinkFrBack(&relinker);
271 : : relinker.relink(new AstCvtArrayToPacked{
272 : : nodep->fileline(), nodep,
273 : : nodep->findLogicDType(unpackBits, unpackMinBits, VSigning::UNSIGNED)});
274 : : }
275 : : }
276 : : // When fromp() is a DType (e.g. unlinked RefDType), resolve through
277 : : // the ref chain; when it's an expression, dtypep() is already resolved.
278 : : static AstNodeDType* fromDTypep(AstNode* fromp) {
279 : : if (AstNodeDType* const dtypep = VN_CAST(fromp, NodeDType))
280 : : return dtypep->skipRefOrNullp();
281 : : return fromp ? fromp->dtypep() : nullptr;
282 : : }
283 : : // VISITORS
284 : : // Naming: width_O{outputtype}_L{lhstype}_R{rhstype}_W{widthing}_S{signing}
285 : : // Where type:
286 : : // _O1=boolean (width 1 unsigned)
287 : : // _Ou=unsigned
288 : : // _Os=signed
289 : : // _Ous=unsigned or signed
290 : : // _Or=real
291 : : // _Ox=anything
292 : :
293 : : // Widths: 1 bit out, lhs 1 bit; Real: converts via compare with 0
294 : : void visit(AstLogNot* nodep) override { visit_log_not(nodep); }
295 : : // Widths: 1 bit out, lhs 1 bit, rhs 1 bit; Real: converts via compare with 0
296 : : void visit(AstLogAnd* nodep) override { visit_log_and_or(nodep); }
297 : : void visit(AstLogOr* nodep) override { visit_log_and_or(nodep); }
298 : : // Sequence and/or/intersect (IEEE 1800-2023 16.9.2), same width rules as LogAnd/LogOr
299 : : void visit(AstSAnd* nodep) override { visit_log_and_or(nodep); }
300 : : void visit(AstSIntersect* nodep) override { visit_log_and_or(nodep); }
301 : : void visit(AstSOr* nodep) override { visit_log_and_or(nodep); }
302 : : void visit(AstSWithin* nodep) override { visit_log_and_or(nodep); }
303 : : void visit(AstLogEq* nodep) override {
304 : : // Conversion from real not in IEEE, but a fallout
305 : : visit_log_and_or(nodep);
306 : : }
307 : : void visit(AstLogIf* nodep) override {
308 : : // Conversion from real not in IEEE, but a fallout
309 : : visit_log_and_or(nodep);
310 : : }
311 : :
312 : : // Widths: 1 bit out, Any width lhs
313 : : void visit(AstRedAnd* nodep) override { visit_red_and_or(nodep); }
314 : : void visit(AstRedOr* nodep) override { visit_red_and_or(nodep); }
315 : : void visit(AstRedXor* nodep) override { visit_red_and_or(nodep); }
316 : : void visit(AstOneHot* nodep) override { visit_red_and_or(nodep); }
317 : : void visit(AstOneHot0* nodep) override { visit_red_and_or(nodep); }
318 : : void visit(AstIsUnknown* nodep) override {
319 : : visit_red_unknown(nodep); // Allow real
320 : : }
321 : :
322 : : // These have different node types, as they operate differently
323 : : // Must add to case statement below,
324 : : // Widths: 1 bit out, lhs width == rhs width. real if lhs|rhs real
325 : : void visit(AstEq* nodep) override { visit_cmp_eq_gt(nodep, true); }
326 : : void visit(AstNeq* nodep) override { visit_cmp_eq_gt(nodep, true); }
327 : : void visit(AstGt* nodep) override { visit_cmp_eq_gt(nodep, true); }
328 : : void visit(AstGte* nodep) override { visit_cmp_eq_gt(nodep, true); }
329 : : void visit(AstLt* nodep) override { visit_cmp_eq_gt(nodep, true); }
330 : : void visit(AstLte* nodep) override { visit_cmp_eq_gt(nodep, true); }
331 : : void visit(AstGtS* nodep) override { visit_cmp_eq_gt(nodep, true); }
332 : : void visit(AstGteS* nodep) override { visit_cmp_eq_gt(nodep, true); }
333 : : void visit(AstLtS* nodep) override { visit_cmp_eq_gt(nodep, true); }
334 : : void visit(AstLteS* nodep) override { visit_cmp_eq_gt(nodep, true); }
335 : : void visit(AstEqCase* nodep) override { visit_cmp_eq_gt(nodep, true); }
336 : : void visit(AstNeqCase* nodep) override { visit_cmp_eq_gt(nodep, true); }
337 : : // ... These comparisons don't allow reals
338 : : void visit(AstEqWild* nodep) override { visit_cmp_eq_gt(nodep, false); }
339 : : void visit(AstNeqWild* nodep) override { visit_cmp_eq_gt(nodep, false); }
340 : : // ... Real compares
341 : : void visit(AstEqD* nodep) override { visit_cmp_real(nodep); }
342 : : void visit(AstNeqD* nodep) override { visit_cmp_real(nodep); }
343 : : void visit(AstLtD* nodep) override { visit_cmp_real(nodep); }
344 : : void visit(AstLteD* nodep) override { visit_cmp_real(nodep); }
345 : : void visit(AstGtD* nodep) override { visit_cmp_real(nodep); }
346 : : void visit(AstGteD* nodep) override { visit_cmp_real(nodep); }
347 : : // ... String compares
348 : : void visit(AstEqN* nodep) override { visit_cmp_string(nodep); }
349 : : void visit(AstNeqN* nodep) override { visit_cmp_string(nodep); }
350 : : void visit(AstLtN* nodep) override { visit_cmp_string(nodep); }
351 : : void visit(AstLteN* nodep) override { visit_cmp_string(nodep); }
352 : : void visit(AstGtN* nodep) override { visit_cmp_string(nodep); }
353 : : void visit(AstGteN* nodep) override { visit_cmp_string(nodep); }
354 : : // ... Data type compares
355 : : void visit(AstEqT* nodep) override { visit_cmp_type(nodep); }
356 : : void visit(AstNeqT* nodep) override { visit_cmp_type(nodep); }
357 : :
358 : : // Widths: out width = lhs width = rhs width
359 : : // Signed: Output signed iff LHS & RHS signed.
360 : : // Real: Not allowed
361 : : void visit(AstAnd* nodep) override { visit_boolexpr_and_or(nodep); }
362 : : void visit(AstOr* nodep) override { visit_boolexpr_and_or(nodep); }
363 : : void visit(AstXor* nodep) override { visit_boolexpr_and_or(nodep); }
364 : : void visit(AstBufIf1* nodep) override {
365 : : visit_boolexpr_and_or(nodep);
366 : : } // Signed behavior changing in 3.814
367 : : // Width: Max(Lhs,Rhs) sort of.
368 : : // Real: If either side real
369 : : // Signed: If both sides real
370 : : void visit(AstAdd* nodep) override { visit_add_sub_replace(nodep, true); }
371 : : void visit(AstSub* nodep) override { visit_add_sub_replace(nodep, true); }
372 : : void visit(AstDiv* nodep) override { visit_add_sub_replace(nodep, true); }
373 : : void visit(AstMul* nodep) override { visit_add_sub_replace(nodep, true); }
374 : : // These can't promote to real
375 : : void visit(AstModDiv* nodep) override { visit_add_sub_replace(nodep, false); }
376 : : void visit(AstModDivS* nodep) override { visit_add_sub_replace(nodep, false); }
377 : : void visit(AstMulS* nodep) override { visit_add_sub_replace(nodep, false); }
378 : : void visit(AstDivS* nodep) override { visit_add_sub_replace(nodep, false); }
379 : : // Widths: out width = lhs width, but upper matters
380 : : // Signed: Output signed iff LHS signed; unary operator
381 : : // Unary promote to real
382 : : void visit(AstNegate* nodep) override { visit_negate_not(nodep, true); }
383 : : // Unary never real
384 : : void visit(AstNot* nodep) override { visit_negate_not(nodep, false); }
385 : :
386 : : // Real: inputs and output real
387 : : void visit(AstAddD* nodep) override { visit_real_add_sub(nodep); }
388 : : void visit(AstSubD* nodep) override { visit_real_add_sub(nodep); }
389 : : void visit(AstDivD* nodep) override { visit_real_add_sub(nodep); }
390 : : void visit(AstMulD* nodep) override { visit_real_add_sub(nodep); }
391 : : void visit(AstPowD* nodep) override { visit_real_add_sub(nodep); }
392 : : void visit(AstNodeSystemBiopD* nodep) override { visit_real_add_sub(nodep); }
393 : : // Real: Output real
394 : : void visit(AstNegateD* nodep) override { visit_real_neg_ceil(nodep); }
395 : : void visit(AstNodeSystemUniopD* nodep) override { visit_real_neg_ceil(nodep); }
396 : :
397 : : // Widths: out signed/unsigned width = lhs width, input un|signed
398 : : void visit(AstSigned* nodep) override { visit_signed_unsigned(nodep, VSigning::SIGNED); }
399 : : void visit(AstUnsigned* nodep) override { visit_signed_unsigned(nodep, VSigning::UNSIGNED); }
400 : :
401 : : // Widths: Output width from lhs, rhs<33 bits
402 : : // Signed: If lhs signed
403 : : void visit(AstShiftL* nodep) override { visit_shift(nodep); }
404 : : void visit(AstShiftR* nodep) override { visit_shift(nodep); }
405 : : // ShiftRS converts to ShiftR, but not vice-versa
406 : : void visit(AstShiftRS* nodep) override { visit_shift(nodep); }
407 : :
408 : : //========
409 : : // Widths: Output real, input integer signed
410 : : void visit(AstBitsToRealD* nodep) override { visit_Or_Lu64(nodep); }
411 : :
412 : : // Widths: Output integer signed, input real
413 : : void visit(AstRToIS* nodep) override { visit_Os32_Lr(nodep); }
414 : : void visit(AstRToIRoundS* nodep) override {
415 : : // Only created here, size comes from upper expression
416 : : assertAtExpr(nodep);
417 : : if (m_vup->prelim()) { // First stage evaluation
418 : : iterateCheckReal(nodep, "LHS", nodep->lhsp(), BOTH);
419 : : }
420 : : UASSERT_OBJ(nodep->dtypep()->widthSized(), nodep, "RToIRoundS should be presized");
421 : : }
422 : :
423 : : // Widths: Output integer unsigned, input real
424 : : void visit(AstRealToBits* nodep) override { visit_Ou64_Lr(nodep); }
425 : :
426 : : // Output integer, input string
427 : : void visit(AstLenN* nodep) override {
428 : : // Widths: 32 bit out
429 : : UASSERT_OBJ(nodep->lhsp(), nodep, "For unary ops only!");
430 : : if (m_vup->prelim()) {
431 : : // See similar handling in visit_cmp_eq_gt where created
432 : : iterateCheckString(nodep, "LHS", nodep->lhsp(), BOTH);
433 : : nodep->dtypeSetSigned32();
434 : : }
435 : : }
436 : : void visit(AstPutcN* nodep) override {
437 : : // CALLER: str.putc()
438 : : UASSERT_OBJ(nodep->rhsp() && nodep->thsp(), nodep, "For ternary ops only!");
439 : : if (m_vup && m_vup->prelim()) {
440 : : // See similar handling in visit_cmp_eq_gt where created
441 : : iterateCheckString(nodep, "LHS", nodep->lhsp(), BOTH);
442 : : iterateCheckSigned32(nodep, "RHS", nodep->rhsp(), BOTH);
443 : : iterateCheckSigned8(nodep, "THS", nodep->thsp(), BOTH);
444 : : nodep->dtypeSetString(); // AstPutcN returns the new string to be assigned by
445 : : // AstAssign
446 : : }
447 : : }
448 : : void visit(AstGetcN* nodep) override {
449 : : // CALLER: str.getc()
450 : : UASSERT_OBJ(nodep->rhsp(), nodep, "For binary ops only!");
451 : : if (m_vup && m_vup->prelim()) {
452 : : // See similar handling in visit_cmp_eq_gt where created
453 : : iterateCheckString(nodep, "LHS", nodep->lhsp(), BOTH);
454 : : iterateCheckSigned32(nodep, "RHS", nodep->rhsp(), BOTH);
455 : : nodep->dtypeSetBitSized(8, VSigning::UNSIGNED);
456 : : }
457 : : }
458 : : void visit(AstGetcRefN* nodep) override {
459 : : // CALLER: str.getc()
460 : : UASSERT_OBJ(nodep->rhsp(), nodep, "For binary ops only!");
461 : : if (m_vup && m_vup->prelim()) {
462 : : // See similar handling in visit_cmp_eq_gt where created
463 : : iterateCheckString(nodep, "LHS", nodep->lhsp(), BOTH);
464 : : iterateCheckSigned32(nodep, "RHS", nodep->rhsp(), BOTH);
465 : : nodep->dtypeSetBitSized(8, VSigning::UNSIGNED);
466 : : }
467 : : }
468 : : void visit(AstSubstrN* nodep) override {
469 : : // CALLER: str.substr()
470 : : UASSERT_OBJ(nodep->rhsp() && nodep->thsp(), nodep, "For ternary ops only!");
471 : : if (m_vup && m_vup->prelim()) {
472 : : // See similar handling in visit_cmp_eq_gt where created
473 : : iterateCheckString(nodep, "LHS", nodep->lhsp(), BOTH);
474 : : iterateCheckSigned32(nodep, "RHS", nodep->rhsp(), BOTH);
475 : : iterateCheckSigned32(nodep, "THS", nodep->thsp(), BOTH);
476 : : nodep->dtypeSetString();
477 : : }
478 : : }
479 : : void visit(AstCompareNN* nodep) override {
480 : : // CALLER: str.compare(), str.icompare()
481 : : // Widths: 32 bit out
482 : : UASSERT_OBJ(nodep->rhsp(), nodep, "For binary ops only!");
483 : : assertAtExpr(nodep);
484 : : if (m_vup->prelim()) {
485 : : // See similar handling in visit_cmp_eq_gt where created
486 : : iterateCheckString(nodep, "LHS", nodep->lhsp(), BOTH);
487 : : iterateCheckString(nodep, "RHS", nodep->rhsp(), BOTH);
488 : : nodep->dtypeSetSigned32();
489 : : }
490 : : }
491 : : void visit(AstAtoN* nodep) override {
492 : : // CALLER: str.atobin(), atoi(), atohex(), atooct(), atoreal()
493 : : // Width: 64bit floating point for atoreal(), 32bit out for the others
494 : : assertAtExpr(nodep);
495 : : if (m_vup->prelim()) {
496 : : // See similar handling in visit_cmp_eq_gt where created
497 : : iterateCheckString(nodep, "LHS", nodep->lhsp(), BOTH);
498 : : if (nodep->format() == AstAtoN::ATOREAL) {
499 : : nodep->dtypeSetDouble();
500 : : } else {
501 : : nodep->dtypeSetSigned32();
502 : : }
503 : : }
504 : : }
505 : : void visit(AstNToI* nodep) override {
506 : : // Created here, should be already sized
507 : : assertAtExpr(nodep);
508 : : if (m_vup->prelim()) {
509 : : UASSERT_OBJ(nodep->dtypep(), nodep, "NToI should be sized when created");
510 : : iterateCheckString(nodep, "LHS", nodep->lhsp(), BOTH);
511 : : }
512 : : }
513 : :
514 : : // Widths: Constant, terminal
515 : : void visit(AstTime* nodep) override { nodep->dtypeSetUInt64(); }
516 : : void visit(AstTimeD* nodep) override { nodep->dtypeSetDouble(); }
517 : : void visit(AstTimePrecision* nodep) override { nodep->dtypeSetSigned32(); }
518 : : void visit(AstGetInitialRandomSeed* nodep) override { nodep->dtypeSetSigned32(); }
519 : : void visit(AstTimeUnit* nodep) override {
520 : : nodep->replaceWith(
521 : : new AstConst{nodep->fileline(), AstConst::Signed32{}, nodep->timeunit().powerOfTen()});
522 : : VL_DO_DANGLING(pushDeletep(nodep), nodep);
523 : : }
524 : : void visit(AstScopeName* nodep) override {
525 : : nodep->dtypeSetUInt64(); // A pointer, but not that it matters
526 : : }
527 : :
528 : : void visit(AstCond* nodep) override {
529 : : // op = cond ? expr1 : expr2
530 : : // See IEEE-2012 11.4.11 and Table 11-21.
531 : : // LHS is self-determined
532 : : // Width: max(RHS, THS)
533 : : // Signed: Output signed iff RHS & THS signed (presumed, not in IEEE)
534 : : // Real: Output real if either expression is real, non-real argument gets converted
535 : : assertAtExpr(nodep);
536 : : if (m_vup->prelim()) { // First stage evaluation
537 : : // Just once, do the conditional, expect one bit out.
538 : : iterateCheckBool(nodep, "Conditional Test", nodep->condp(), BOTH);
539 : : // Determine sub expression widths only relying on what's in the subops
540 : : // CONTEXT_DET determined, but need data type for pattern assignments
541 : : userIterateAndNext(nodep->thenp(), WidthVP{m_vup->dtypeNullp(), PRELIM}.p());
542 : : userIterateAndNext(nodep->elsep(), WidthVP{m_vup->dtypeNullp(), PRELIM}.p());
543 : : // Calculate width of this expression.
544 : : // First call (prelim()) m_vup->width() is probably zero, so we'll return
545 : : // the size of this subexpression only.
546 : : // Second call (final()) m_vup->width() is probably the expression size, so
547 : : // the expression includes the size of the output too.
548 : : const AstNodeDType* const thenDTypep = nodep->thenp()->dtypep();
549 : : const AstNodeDType* const elseDTypep = nodep->elsep()->dtypep();
550 : : if (nodep->thenp()->isNull() && nodep->elsep()->isNull()) {
551 : : nodep->dtypep(m_vup->dtypeNullp());
552 : : } else if (thenDTypep->skipRefp() == elseDTypep->skipRefp()) {
553 : : // TODO might need a broader equation, use the Castable function?
554 : : nodep->dtypeFrom(thenDTypep);
555 : : } else if (nodep->thenp()->isClassHandleValue()
556 : : || nodep->elsep()->isClassHandleValue()) {
557 : : AstNodeDType* commonClassTypep = nullptr;
558 : : if (nodep->thenp()->isClassHandleValue() && nodep->elsep()->isClassHandleValue()) {
559 : : // Get the most-deriving class type that both arguments can be casted to.
560 : : commonClassTypep
561 : : = AstNode::getCommonClassTypep(nodep->thenp(), nodep->elsep());
562 : : }
563 : : if (commonClassTypep) {
564 : : nodep->dtypep(commonClassTypep);
565 : : } else {
566 : : nodep->v3error("Incompatible types of operands of condition operator: "
567 : : << thenDTypep->prettyTypeName() << " and "
568 : : << elseDTypep->prettyTypeName());
569 : : nodep->dtypeFrom(thenDTypep);
570 : : }
571 : : } else if (nodep->thenp()->isDouble() || nodep->elsep()->isDouble()) {
572 : : nodep->dtypeSetDouble();
573 : : } else if (nodep->thenp()->isString() || nodep->elsep()->isString()) {
574 : : nodep->dtypeSetString();
575 : : } else {
576 : : const int width = std::max(nodep->thenp()->width(), nodep->elsep()->width());
577 : : const int mwidth
578 : : = std::max(nodep->thenp()->widthMin(), nodep->elsep()->widthMin());
579 : : const bool issigned = nodep->thenp()->isSigned() && nodep->elsep()->isSigned();
580 : : nodep->dtypeSetLogicUnsized(width, mwidth, VSigning::fromBool(issigned));
581 : : }
582 : : }
583 : : if (m_vup->final()) {
584 : : AstNodeDType* const expDTypep = m_vup->dtypeOverridep(nodep->dtypep());
585 : : AstNodeDType* const subDTypep = expDTypep;
586 : : nodep->dtypep(expDTypep);
587 : : // Error report and change sizes for suboperands of this node.
588 : : iterateCheck(nodep, "Conditional True", nodep->thenp(), CONTEXT_DET, FINAL, subDTypep,
589 : : EXTEND_EXP);
590 : : iterateCheck(nodep, "Conditional False", nodep->elsep(), CONTEXT_DET, FINAL, subDTypep,
591 : : EXTEND_EXP);
592 : : }
593 : : }
594 : : void visit(AstConcat* nodep) override {
595 : : // Real: Not allowed (assumed)
596 : : // Signed: unsigned output, input either (assumed)
597 : : // IEEE-2012 Table 11-21, and 11.8.1:
598 : : // LHS, RHS is self-determined
599 : : // signed: Unsigned (11.8.1)
600 : : // width: LHS + RHS
601 : : AstNodeDType* const vdtypep = m_vup->dtypeNullSkipRefp();
602 : : userIterate(vdtypep, WidthVP{SELF, BOTH}.p());
603 : : // Conversions
604 : : if (const AstDynArrayDType* const adtypep = VN_CAST(vdtypep, DynArrayDType)) {
605 : : // IEEE 1800-2023 10.10 requires looking at argument data type
606 : : // to determine if value or push
607 : : userIterateAndNext(nodep->lhsp(), WidthVP{vdtypep, PRELIM}.p());
608 : : userIterateAndNext(nodep->rhsp(), WidthVP{vdtypep, PRELIM}.p());
609 : : // Queue "element 0" is lhsp, so we need to swap arguments
610 : : const bool lhsIsValue
611 : : = AstNode::computeCastable(adtypep->subDTypep(), nodep->lhsp()->dtypep(), nullptr)
612 : : .isAssignable();
613 : : const bool rhsIsValue
614 : : = AstNode::computeCastable(adtypep->subDTypep(), nodep->rhsp()->dtypep(), nullptr)
615 : : .isAssignable();
616 : : AstConsDynArray* const newp
617 : : = new AstConsDynArray{nodep->fileline(), rhsIsValue, nodep->rhsp()->unlinkFrBack(),
618 : : lhsIsValue, nodep->lhsp()->unlinkFrBack()};
619 : : nodep->replaceWith(newp);
620 : : VL_DO_DANGLING(pushDeletep(nodep), nodep);
621 : : userIterateChildren(newp, m_vup);
622 : : return;
623 : : }
624 : : if (const AstQueueDType* const adtypep = VN_CAST(vdtypep, QueueDType)) {
625 : : // IEEE 1800-2023 10.10 requires looking at argument data type
626 : : // to determine if value or push
627 : : userIterateAndNext(nodep->lhsp(), WidthVP{vdtypep, PRELIM}.p());
628 : : userIterateAndNext(nodep->rhsp(), WidthVP{vdtypep, PRELIM}.p());
629 : : const bool lhsIsValue
630 : : = AstNode::computeCastable(adtypep->subDTypep(), nodep->lhsp()->dtypep(), nullptr)
631 : : .isAssignable();
632 : : const bool rhsIsValue
633 : : = AstNode::computeCastable(adtypep->subDTypep(), nodep->rhsp()->dtypep(), nullptr)
634 : : .isAssignable();
635 : : AstConsQueue* const newp
636 : : = new AstConsQueue{nodep->fileline(), rhsIsValue, nodep->rhsp()->unlinkFrBack(),
637 : : lhsIsValue, nodep->lhsp()->unlinkFrBack()};
638 : : nodep->replaceWith(newp);
639 : : VL_DO_DANGLING(pushDeletep(nodep), nodep);
640 : : userIterateChildren(newp, m_vup);
641 : : return;
642 : : }
643 : : if (VN_IS(vdtypep, UnpackArrayDType)) {
644 : : AstPattern* const newp = new AstPattern{nodep->fileline(), nullptr};
645 : : patConcatConvertRecurse(newp, nodep);
646 : : nodep->replaceWith(newp);
647 : : VL_DO_DANGLING(pushDeletep(nodep), nodep);
648 : : userIterate(newp, m_vup);
649 : : return;
650 : : }
651 : :
652 : : // Concat handling
653 : : if (m_vup->prelim()) {
654 : : if (VN_IS(vdtypep, AssocArrayDType) //
655 : : || VN_IS(vdtypep, DynArrayDType) //
656 : : || VN_IS(vdtypep, QueueDType)) {
657 : : nodep->v3warn(E_UNSUPPORTED, "Unsupported: Concatenation to form "
658 : : << vdtypep->prettyDTypeNameQ() << " data type");
659 : : }
660 : :
661 : : if (vdtypep && vdtypep->isString()) {
662 : : iterateCheckString(nodep, "LHS", nodep->lhsp(), BOTH);
663 : : iterateCheckString(nodep, "RHS", nodep->rhsp(), BOTH);
664 : : nodep->dtypeSetString();
665 : : } else {
666 : : iterateCheckIntegralSelf(nodep, "LHS", nodep->lhsp(), SELF, BOTH);
667 : : iterateCheckIntegralSelf(nodep, "RHS", nodep->rhsp(), SELF, BOTH);
668 : :
669 : : if (m_streamConcat) {
670 : : packIfUnpacked(nodep->lhsp());
671 : : packIfUnpacked(nodep->rhsp());
672 : : }
673 : : nodep->dtypeSetLogicUnsized(nodep->lhsp()->width() + nodep->rhsp()->width(),
674 : : nodep->lhsp()->widthMin() + nodep->rhsp()->widthMin(),
675 : : VSigning::UNSIGNED);
676 : : }
677 : : // Cleanup zero width Verilog2001 {x,{0{foo}}} now,
678 : : // otherwise having width(0) will cause later assertions to fire
679 : : if (const AstReplicate* const repp = VN_CAST(nodep->lhsp(), Replicate)) {
680 : : if (repp->width() == 0) { // Keep rhs
681 : : nodep->replaceWith(nodep->rhsp()->unlinkFrBack());
682 : : VL_DO_DANGLING(pushDeletep(nodep), nodep);
683 : : return;
684 : : }
685 : : }
686 : : if (const AstReplicate* const repp = VN_CAST(nodep->rhsp(), Replicate)) {
687 : : if (repp->width() == 0) { // Keep lhs
688 : : nodep->replaceWith(nodep->lhsp()->unlinkFrBack());
689 : : VL_DO_DANGLING(pushDeletep(nodep), nodep);
690 : : return;
691 : : }
692 : : }
693 : : }
694 : : if (m_vup->final()) {
695 : : if (nodep->lhsp()->isString() || nodep->rhsp()->isString()) {
696 : : AstNodeExpr* lhsp = nodep->lhsp()->unlinkFrBack();
697 : : AstNodeExpr* rhsp = nodep->rhsp()->unlinkFrBack();
698 : : if (!lhsp->isString()) lhsp = new AstCvtPackString{lhsp->fileline(), lhsp};
699 : : if (!rhsp->isString()) rhsp = new AstCvtPackString{rhsp->fileline(), rhsp};
700 : : AstNode* const newp = new AstConcatN{nodep->fileline(), lhsp, rhsp};
701 : : nodep->replaceWith(newp);
702 : : VL_DO_DANGLING(pushDeletep(nodep), nodep);
703 : : return;
704 : : }
705 : : if (!nodep->dtypep()->widthSized()) {
706 : : // See also error in V3Number
707 : : nodeForUnsizedWarning(nodep)->v3warn(
708 : : WIDTHCONCAT, "Unsized numbers/parameters not allowed in concatenations.");
709 : : }
710 : : }
711 : : }
712 : : void visit(AstConcatN* nodep) override {
713 : : if (nodep->didWidth()) return;
714 : : // String concatenate.
715 : : // Already did AstConcat simplifications
716 : : assertAtExpr(nodep);
717 : : if (m_vup->prelim()) {
718 : : iterateCheckString(nodep, "LHS", nodep->lhsp(), BOTH);
719 : : iterateCheckString(nodep, "RHS", nodep->rhsp(), BOTH);
720 : : nodep->dtypeSetString();
721 : : }
722 : : if (m_vup->final()) {
723 : : nodep->didWidth(true);
724 : : if (!nodep->dtypep()->widthSized()) {
725 : : // See also error in V3Number
726 : : nodeForUnsizedWarning(nodep)->v3warn(
727 : : WIDTHCONCAT, "Unsized numbers/parameters not allowed in concatenations.");
728 : : }
729 : : }
730 : : }
731 : : void visit(AstDefaultDisable* nodep) override {
732 : : assertAtStatement(nodep);
733 : : // it's like an if() condition.
734 : : iterateCheckBool(nodep, "default disable iff condition", nodep->condp(), BOTH);
735 : : }
736 : : void visit(AstDelay* nodep) override {
737 : : if (AstNodeExpr* const fallDelayp = nodep->fallDelay()) {
738 : : iterateCheckDelay(nodep, "delay", nodep->lhsp(), BOTH);
739 : : iterateCheckDelay(nodep, "delay", fallDelayp, BOTH);
740 : : return;
741 : : }
742 : : if (VN_IS(m_procedurep, Final)) {
743 : : nodep->v3error("Delays are not legal in final blocks (IEEE 1800-2023 9.2.3)");
744 : : VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
745 : : return;
746 : : }
747 : : if (!m_underFork && VN_IS(m_ftaskp, Func)) {
748 : : nodep->v3error("Delays are not legal in functions. Suggest use a task "
749 : : "(IEEE 1800-2023 13.4.4)");
750 : : VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
751 : : return;
752 : : }
753 : : if (nodep->fileline()->timingOn()) {
754 : : if (v3Global.opt.timing().isSetTrue()) {
755 : : iterateCheckDelay(nodep, "delay", nodep->lhsp(), BOTH);
756 : : iterateAndNextNull(nodep->stmtsp());
757 : : return;
758 : : } else if (v3Global.opt.timing().isSetFalse()) {
759 : : nodep->v3warn(STMTDLY, "Ignoring delay on this statement due to --no-timing");
760 : : } else {
761 : : nodep->v3warn(
762 : : E_NEEDTIMINGOPT,
763 : : "Use --timing or --no-timing to specify how delays should be handled");
764 : : }
765 : : }
766 : : if (nodep->stmtsp()) nodep->addNextHere(nodep->stmtsp()->unlinkFrBackWithNext());
767 : : VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
768 : : }
769 : : void visit(AstFireEvent* nodep) override {
770 : : assertAtStatement(nodep);
771 : : iterateCheckSelf(nodep, "LHS", nodep->operandp(), SELF, BOTH);
772 : : }
773 : : void visit(AstFork* nodep) override {
774 : : if (!m_underFork && VN_IS(m_ftaskp, Func) && !nodep->joinType().joinNone()) {
775 : : nodep->v3error("Only fork .. join_none is legal in functions. "
776 : : "(IEEE 1800-2023 13.4.4)");
777 : : VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
778 : : return;
779 : : }
780 : : if (!nodep->fileline()->timingOn()
781 : : // With no statements, begin is identical
782 : : || !nodep->forksp()
783 : : || (!v3Global.opt.timing().isSetTrue() // If no --timing
784 : : && (v3Global.opt.bboxUnsup()
785 : : // With one statement and no timing, a begin block does as good as a
786 : : // fork/join or join_any
787 : : || (!nodep->forksp()->nextp() && !nodep->joinType().joinNone())))) {
788 : : AstBegin* const newp = new AstBegin{nodep->fileline(), nodep->name(), nullptr, false};
789 : : nodep->replaceWith(newp);
790 : : if (nodep->declsp()) newp->addDeclsp(nodep->declsp()->unlinkFrBackWithNext());
791 : : if (nodep->stmtsp()) newp->addStmtsp(nodep->stmtsp()->unlinkFrBackWithNext());
792 : : if (nodep->forksp()) newp->addStmtsp(nodep->forksp()->unlinkFrBackWithNext());
793 : : VL_DO_DANGLING(nodep->deleteTree(), nodep);
794 : : } else if (v3Global.opt.timing().isSetTrue()) {
795 : : VL_RESTORER(m_underFork);
796 : : m_underFork = true;
797 : : iterateChildren(nodep);
798 : : } else if (v3Global.opt.timing().isSetFalse()) {
799 : : nodep->v3warn(E_NOTIMING, "Fork statements require --timing");
800 : : VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
801 : : } else {
802 : : nodep->v3warn(E_NEEDTIMINGOPT,
803 : : "Use --timing or --no-timing to specify how forks should be handled");
804 : : }
805 : : }
806 : : void visit(AstDisableFork* nodep) override {
807 : : if (nodep->fileline()->timingOn()) {
808 : : if (v3Global.opt.timing().isSetFalse()) {
809 : : nodep->v3warn(E_NOTIMING, "Support for disable fork statement requires --timing");
810 : : VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
811 : : } else if (!v3Global.opt.timing().isSetTrue()) {
812 : : nodep->v3warn(E_NEEDTIMINGOPT, "Use --timing or --no-timing to specify how "
813 : : << "disable fork should be handled");
814 : : }
815 : : }
816 : : }
817 : : void visit(AstWaitFork* nodep) override {
818 : : if (nodep->fileline()->timingOn()) {
819 : : if (v3Global.opt.timing().isSetFalse()) {
820 : : nodep->v3warn(E_NOTIMING, "Support for disable fork statement requires --timing");
821 : : VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
822 : : } else if (!v3Global.opt.timing().isSetTrue()) {
823 : : nodep->v3warn(E_NEEDTIMINGOPT, "Use --timing or --no-timing to specify how "
824 : : << "disable fork should be handled");
825 : : }
826 : : }
827 : : }
828 : : void visit(AstToLowerN* nodep) override {
829 : : assertAtExpr(nodep);
830 : : if (m_vup->prelim()) {
831 : : iterateCheckString(nodep, "LHS", nodep->lhsp(), BOTH);
832 : : nodep->dtypeSetString();
833 : : }
834 : : }
835 : : void visit(AstToUpperN* nodep) override {
836 : : assertAtExpr(nodep);
837 : : if (m_vup->prelim()) {
838 : : iterateCheckString(nodep, "LHS", nodep->lhsp(), BOTH);
839 : : nodep->dtypeSetString();
840 : : }
841 : : }
842 : : void visit(AstReplicate* nodep) override {
843 : : // IEEE-2012 Table 11-21:
844 : : // LHS, RHS is self-determined
845 : : // width: value(LHS) * width(RHS)
846 : : assertAtExpr(nodep);
847 : : if (m_vup->prelim()) {
848 : : iterateCheckSizedSelf(nodep, "RHS", nodep->countp(), SELF, BOTH);
849 : : V3Const::constifyParamsNoWarnEdit(nodep->countp()); // rhsp may change
850 : :
851 : : int32_t times = 1; // IEEE replicate value is integral
852 : :
853 : : const AstConst* const constp = VN_CAST(nodep->countp(), Const);
854 : : if (constp) {
855 : : if (constp->num().isFourState()
856 : : || (constp->dtypep()->isSigned() && constp->num().isNegative())) {
857 : : nodep->v3error("Replication value of < 0 or X/Z not legal"
858 : : " (IEEE 1800-2023 11.4.12.1): "
859 : : << constp->prettyNameQ());
860 : : } else {
861 : : times = constp->toSInt();
862 : : }
863 : : }
864 : :
865 : : AstNodeDType* const vdtypep = m_vup->dtypeNullSkipRefp();
866 : : if (VN_IS(vdtypep, QueueDType) || VN_IS(vdtypep, DynArrayDType)
867 : : || VN_IS(vdtypep, UnpackArrayDType)) {
868 : : if (times != 1) {
869 : : nodep->v3warn(E_UNSUPPORTED, "Unsupported: Non-1 replication to form "
870 : : << vdtypep->prettyDTypeNameQ()
871 : : << " data type");
872 : : }
873 : : if (VN_IS(nodep->srcp(), Concat)) {
874 : : // Convert to concat directly, and visit(AstConst) will convert.
875 : : // Don't iterate lhsp as SELF, the potential Concat below needs
876 : : // the adtypep passed down to recognize the QueueDType
877 : : userIterateAndNext(nodep->srcp(), WidthVP{vdtypep, BOTH}.p());
878 : : nodep->replaceWith(nodep->srcp()->unlinkFrBack());
879 : : VL_DO_DANGLING(pushDeletep(nodep), nodep);
880 : : return;
881 : : } else { // int a[] = {lhs} -> same as '{lhs}
882 : : AstPattern* const newp = new AstPattern{
883 : : nodep->fileline(),
884 : : new AstPatMember{nodep->srcp()->fileline(), nodep->srcp()->unlinkFrBack(),
885 : : nullptr, nullptr}};
886 : : nodep->replaceWith(newp);
887 : : VL_DO_DANGLING(pushDeletep(nodep), nodep);
888 : : userIterate(newp, m_vup);
889 : : return;
890 : : }
891 : : }
892 : : if (VN_IS(vdtypep, AssocArrayDType)) {
893 : : nodep->v3warn(E_UNSUPPORTED, "Unsupported: Replication to form "
894 : : << vdtypep->prettyDTypeNameQ() << " data type");
895 : : }
896 : : if (vdtypep && vdtypep->isString()) {
897 : : iterateCheckString(nodep, "LHS", nodep->srcp(), BOTH);
898 : : } else {
899 : : iterateCheckIntegralSelf(nodep, "LHS", nodep->srcp(), SELF, BOTH);
900 : : }
901 : :
902 : : if ((vdtypep && vdtypep->isString()) || nodep->srcp()->isString()) {
903 : : AstNode* const newp
904 : : = new AstReplicateN{nodep->fileline(), nodep->srcp()->unlinkFrBack(),
905 : : nodep->countp()->unlinkFrBack()};
906 : : nodep->replaceWith(newp);
907 : : VL_DO_DANGLING(pushDeletep(nodep), nodep);
908 : : return;
909 : : } else {
910 : : if (!constp) nodep->v3error("Replication value isn't a constant.");
911 : : if (times == 0
912 : : && !VN_IS(nodep->backp(), Concat)) { // Concat Visitor will clean it up.
913 : : nodep->v3warn(ZEROREPL,
914 : : "Replication value of 0 is only legal under a concatenation"
915 : : " (IEEE 1800-2023 11.4.12.1)");
916 : : times = 1; // Set to 1, so we can continue looking for errors
917 : : }
918 : : nodep->dtypeSetLogicUnsized((nodep->srcp()->width() * times),
919 : : (nodep->srcp()->widthMin() * times),
920 : : VSigning::UNSIGNED);
921 : : }
922 : : }
923 : : if (m_vup->final()) {
924 : : if (!nodep->dtypep()->widthSized()) {
925 : : // See also error in V3Number
926 : : nodeForUnsizedWarning(nodep)->v3warn(
927 : : WIDTHCONCAT, "Unsized numbers/parameters not allowed in replications.");
928 : : }
929 : : }
930 : : }
931 : : void visit(AstReplicateN* nodep) override {
932 : : // Replicate with string
933 : : assertAtExpr(nodep);
934 : : if (m_vup->prelim()) {
935 : : iterateCheckString(nodep, "LHS", nodep->lhsp(), BOTH);
936 : : iterateCheckSizedSelf(nodep, "RHS", nodep->rhsp(), SELF, BOTH);
937 : : V3Const::constifyParamsNoWarnEdit(nodep->rhsp()); // rhsp may change
938 : : nodep->dtypeSetString();
939 : : }
940 : : if (m_vup->final()) {
941 : : if (!nodep->dtypep()->widthSized()) {
942 : : // See also error in V3Number
943 : : nodeForUnsizedWarning(nodep)->v3warn(
944 : : WIDTHCONCAT, "Unsized numbers/parameters not allowed in replications.");
945 : : }
946 : : }
947 : : }
948 : : void visit(AstNodeDistBiop* nodep) override {
949 : : assertAtExpr(nodep);
950 : : if (m_vup->prelim()) { // First stage evaluation
951 : : iterateCheckSigned32(nodep, "seed", nodep->lhsp(), BOTH);
952 : : iterateCheckSigned32(nodep, "RHS", nodep->rhsp(), BOTH);
953 : : nodep->dtypeSetSigned32();
954 : : }
955 : : }
956 : : void visit(AstNodeDistTriop* nodep) override {
957 : : if (m_vup->prelim()) { // First stage evaluation
958 : : iterateCheckSigned32(nodep, "seed", nodep->lhsp(), BOTH);
959 : : iterateCheckSigned32(nodep, "RHS", nodep->rhsp(), BOTH);
960 : : iterateCheckSigned32(nodep, "THS", nodep->thsp(), BOTH);
961 : : nodep->dtypeSetSigned32();
962 : : }
963 : : }
964 : : void visit(AstNodeStream* nodep) override {
965 : : VL_RESTORER(m_streamConcat);
966 : : // UINFOTREE(1, nodep, "stream-in vup" << m_vup, "stream-in ");
967 : : assertAtExpr(nodep);
968 : : if (m_vup->prelim()) {
969 : : m_streamConcat = true;
970 : : iterateCheckSelf(nodep, "LHS", nodep->lhsp(), SELF, BOTH);
971 : : m_streamConcat = false;
972 : : iterateCheckSelf(nodep, "RHS", nodep->rhsp(), SELF, BOTH);
973 : : V3Const::constifyParamsEdit(nodep->rhsp()); // rhsp may change
974 : : if (const AstConst* const constp = VN_CAST(nodep->rhsp(), Const)) {
975 : : if (constp->toUInt() == 0) nodep->v3error("Slice size cannot be zero.");
976 : : } else {
977 : : nodep->v3error("Slice size isn't a constant or basic data type.");
978 : : }
979 : : const AstNodeDType* const lhsDtypep = nodep->lhsp()->dtypep()->skipRefToEnump();
980 : : if (VN_IS(lhsDtypep, DynArrayDType) || VN_IS(lhsDtypep, QueueDType)
981 : : || VN_IS(lhsDtypep, UnpackArrayDType)) {
982 : : nodep->dtypeSetStream();
983 : : } else if (lhsDtypep->isCompound()) {
984 : : nodep->v3warn(E_UNSUPPORTED,
985 : : "Unsupported: Stream operation on a variable of a type "
986 : : << lhsDtypep->prettyDTypeNameQ());
987 : : nodep->dtypeSetStream();
988 : : } else {
989 : : nodep->dtypeSetLogicUnsized(nodep->lhsp()->width(), nodep->lhsp()->widthMin(),
990 : : VSigning::UNSIGNED);
991 : : }
992 : : }
993 : : if (m_vup->final()) {
994 : : if (!nodep->dtypep()->widthSized()) {
995 : : // See also error in V3Number
996 : : nodeForUnsizedWarning(nodep)->v3warn(
997 : : WIDTHCONCAT, "Unsized numbers/parameters not allowed in streams.");
998 : : }
999 : : }
1000 : : }
1001 : : void visit(AstRange* nodep) override {
1002 : : // Real: Not allowed
1003 : : // Signed: unsigned output, input either
1004 : : // Convert all range values to constants
1005 : : UINFO(6, "RANGE " << nodep);
1006 : : V3Const::constifyParamsEdit(nodep->leftp()); // May relink pointed to node
1007 : : V3Const::constifyParamsEdit(nodep->rightp()); // May relink pointed to node
1008 : : checkConstantOrReplace(nodep->leftp(), true,
1009 : : "left side of bit range isn't a two-state constant"
1010 : : " (IEEE 1800-2023 6.9.1)");
1011 : : checkConstantOrReplace(nodep->rightp(), true,
1012 : : "right side of bit range isn't a two-state constant"
1013 : : " (IEEE 1800-2023 6.9.1)");
1014 : : if (m_vup->prelim()) {
1015 : : // Don't need to iterate because V3Const already constified
1016 : : const int width = nodep->elementsConst();
1017 : : if (nodep->fromBracket() && nodep->leftConst() > nodep->rightConst()) {
1018 : : // From a C-Style '[size]' declaration, due to range handling a '[-1]'
1019 : : // will get transformed to look like a '[0:1]', instead report as error
1020 : : // now we know the datatype is used
1021 : : nodep->v3error("Size of range is '["
1022 : : << (-width + 2)
1023 : : << "]', must be positive integer (IEEE 1800-2023 7.4.2)");
1024 : : } else if (width > (1 << 28)) {
1025 : : nodep->v3error("Width of bit range is huge; vector of over 1 billion bits: 0x"
1026 : : << std::hex << width << std::dec);
1027 : : }
1028 : : // Note width() not set on range; use elementsConst()
1029 : : const bool inDeadModule = m_modep && m_modep->dead();
1030 : : // Suppress ASCRANGE in parameterized template modules whose parameter-dependent
1031 : : // ranges haven't been resolved yet, or when the type has no owning module
1032 : : // (e.g. moved to TypeTable during DepGraph resolution).
1033 : : const bool inParameterizedTemplate
1034 : : = m_modep && (m_modep->dead() || m_modep->parameterizedTemplate());
1035 : : const bool inTypeTable = !m_modep;
1036 : : if (nodep->ascending() && !VN_IS(nodep->backp(), UnpackArrayDType)
1037 : : && !VN_IS(nodep->backp(), Cell) // For cells we warn in V3Inst
1038 : : && !m_paramsOnly // Skip during parameter evaluation
1039 : : && !inDeadModule && !inParameterizedTemplate && !inTypeTable) {
1040 : : nodep->v3warn(ASCRANGE, "Ascending bit range vector: left < right of bit range: ["
1041 : : << nodep->leftConst() << ":" << nodep->rightConst()
1042 : : << "]");
1043 : : }
1044 : : }
1045 : : }
1046 : :
1047 : : void visit(AstSel* nodep) override {
1048 : : // Signed: always unsigned; Real: Not allowed
1049 : : // LSB is self-determined (IEEE 2012 11.5.1)
1050 : : // We also use SELs to shorten a signed constant etc, in this case they are signed.
1051 : : if (nodep->didWidth()) return;
1052 : : assertAtExpr(nodep);
1053 : : if (m_vup->prelim()) {
1054 : : UINFOTREE(9, nodep, "", "selWidth");
1055 : : userIterateAndNext(nodep->fromp(), WidthVP{CONTEXT_DET, PRELIM}.p());
1056 : : userIterateAndNext(nodep->lsbp(), WidthVP{SELF, PRELIM}.p());
1057 : : checkCvtUS(nodep->fromp(), false);
1058 : : iterateCheckSizedSelf(nodep, "Select LHS", nodep->fromp(), SELF, BOTH);
1059 : : int width = nodep->widthConst();
1060 : : if (width <= 0) {
1061 : : nodep->v3error("Width of bit extract must be positive (IEEE 1800-2023 11.5.1)");
1062 : : nodep->dtypeSetBit();
1063 : : return;
1064 : : }
1065 : : UASSERT_OBJ(nodep->dtypep(), nodep, "dtype wasn't set"); // by V3WidthSel
1066 : :
1067 : : // Suppress SELRANGE in parameterized template modules where
1068 : : // parameter-dependent widths haven't been resolved yet.
1069 : : const bool inParameterizedTemplate
1070 : : = m_modep && (m_modep->dead() || m_modep->parameterizedTemplate());
1071 : :
1072 : : if (VN_IS(nodep->lsbp(), Const) && nodep->msbConst() < nodep->lsbConst()) {
1073 : : // Likely impossible given above width check
1074 : : nodep->v3warn(E_UNSUPPORTED,
1075 : : "Unsupported: left < right of bit extract: " // LCOV_EXCL_LINE
1076 : : << nodep->msbConst() << "<" << nodep->lsbConst());
1077 : : width = (nodep->lsbConst() - nodep->msbConst() + 1);
1078 : : nodep->dtypeSetLogicSized(width, VSigning::UNSIGNED);
1079 : : nodep->widthConst(width);
1080 : : pushDeletep(nodep->lsbp());
1081 : : nodep->lsbp()->replaceWith(new AstConst{nodep->lsbp()->fileline(), 0});
1082 : : }
1083 : : // We're extracting, so just make sure the expression is at least wide enough.
1084 : : if (nodep->fromp()->width() < width && !inParameterizedTemplate) {
1085 : : nodep->v3warn(SELRANGE, "Extracting " << width << " bits from only "
1086 : : << nodep->fromp()->width() << " bit number");
1087 : : // Extend it.
1088 : : AstNodeDType* const subDTypep
1089 : : = nodep->findLogicDType(width, width, nodep->fromp()->dtypep()->numeric());
1090 : : widthCheckSized(nodep, "errorless...", nodep->fromp(), subDTypep, EXTEND_EXP,
1091 : : false /*noerror*/);
1092 : : }
1093 : : // Check bit indexes.
1094 : : // What is the MSB? We want the true MSB, not one starting at
1095 : : // 0, because a 4 bit index is required to look at a one-bit
1096 : : // variable[15:15] and 5 bits for [15:-2]
1097 : : int frommsb = nodep->fromp()->width() - 1;
1098 : : int fromlsb = 0;
1099 : : const int elw = nodep->declElWidth(); // Must adjust to tell user bit ranges
1100 : : if (nodep->declRange().ranged()) {
1101 : : frommsb = nodep->declRange().hiMaxSelect() * elw
1102 : : + (elw - 1); // Corrected for negative lsb
1103 : : fromlsb = nodep->declRange().lo() * elw;
1104 : : } else {
1105 : : // nodep->v3fatalSrc("Should have been declRanged in V3WidthSel");
1106 : : }
1107 : : const int selwidth = V3Number::log2b(frommsb + 1 - 1) + 1; // Width to address a bit
1108 : : AstNodeDType* const selwidthDTypep
1109 : : = nodep->findLogicDType(selwidth, selwidth, nodep->lsbp()->dtypep()->numeric());
1110 : : userIterateAndNext(nodep->fromp(), WidthVP{SELF, FINAL}.p());
1111 : : userIterateAndNext(nodep->lsbp(), WidthVP{SELF, FINAL}.p());
1112 : : if (widthBad(nodep->lsbp(), selwidthDTypep) && nodep->lsbp()->width() != 32) {
1113 : : if (!nodep->fileline()->warnIsOff(V3ErrorCode::WIDTH)) {
1114 : : nodep->v3widthWarn(
1115 : : (selwidth / elw), (nodep->lsbp()->width() / elw),
1116 : : "Bit extraction of var["
1117 : : << (frommsb / elw) << ":" << (fromlsb / elw) << "] requires "
1118 : : << (selwidth / elw) << " bit index, not "
1119 : : << (nodep->lsbp()->width() / elw)
1120 : : << (nodep->lsbp()->width() != nodep->lsbp()->widthMin()
1121 : : ? " or " + cvtToStr(nodep->lsbp()->widthMin() / elw)
1122 : : : "")
1123 : : << " bits.");
1124 : : UINFO(1, " Related node: " << nodep);
1125 : : }
1126 : : }
1127 : : if (VN_IS(nodep->lsbp(), Const) && nodep->msbConst() > frommsb) {
1128 : : // See also warning in V3Const
1129 : : // We need to check here, because the widthCheckSized may silently
1130 : : // add another SEL which will lose the out-of-range check
1131 : : //
1132 : : // We don't want to trigger an error here if we are just
1133 : : // evaluating type sizes for a generate block condition. We
1134 : : // should only trigger the error if the out-of-range access is
1135 : : // actually generated.
1136 : : AstNodeVarRef* lrefp = AstNodeVarRef::varRefLValueRecurse(nodep);
1137 : : if (m_doGenerate) {
1138 : : UINFO(5, "Selection index out of range inside generate");
1139 : : } else if (!inParameterizedTemplate) {
1140 : : nodep->v3warn(SELRANGE, "Selection index out of range: "
1141 : : << nodep->msbConst() << ":" << nodep->lsbConst()
1142 : : << " outside " << frommsb << ":" << fromlsb);
1143 : : UINFO(1, " Related node: " << nodep);
1144 : : }
1145 : : if (lrefp) UINFO(9, " Select extend lrefp " << lrefp);
1146 : : if (lrefp && lrefp->access().isWriteOrRW()) {
1147 : : // lvarref[X] = ..., the expression assigned is too wide
1148 : : // WTF to do
1149 : : // Don't change the width of this lhsp, instead propagate up
1150 : : // to upper assign/expression the correct width
1151 : : AstNodeDType* const subDTypep
1152 : : = nodep->findLogicDType(width, width, nodep->fromp()->dtypep()->numeric());
1153 : : widthCheckSized(nodep, "errorless...", nodep->fromp(), subDTypep, EXTEND_EXP,
1154 : : false /*noerror*/);
1155 : : } else {
1156 : : // Extend it
1157 : : const int extendTo = nodep->msbConst() + 1;
1158 : : AstNodeDType* const subDTypep = nodep->findLogicDType(
1159 : : extendTo, extendTo, nodep->fromp()->dtypep()->numeric());
1160 : : widthCheckSized(nodep, "errorless...", nodep->fromp(), subDTypep, EXTEND_EXP,
1161 : : false /*noerror*/);
1162 : : }
1163 : : }
1164 : : // iterate FINAL is two blocks above
1165 : : //
1166 : : // If we have a width problem with GENERATE etc, this will reduce
1167 : : // it down and mask it, so we have no chance of finding a real
1168 : : // error in the future. So don't do this for them.
1169 : : if (!m_doGenerate) {
1170 : : // lsbp() must be self-determined, however for performance
1171 : : // we want the select to be truncated to fit within the
1172 : : // maximum select range, e.g. turn Xs outside of the select
1173 : : // into something fast which pulls from within the array.
1174 : : widthCheckSized(nodep, "Extract Range", nodep->lsbp(), selwidthDTypep, EXTEND_EXP,
1175 : : false /*NOWARN*/);
1176 : : }
1177 : : // UINFOTREE(9, nodep, "", "seldone");
1178 : : }
1179 : : }
1180 : :
1181 : : void visit(AstArraySel* nodep) override {
1182 : : // Signed/Real: Output signed iff LHS signed/real; binary operator
1183 : : // Note by contrast, bit extract selects are unsigned
1184 : : // LSB is self-determined (IEEE 2012 11.5.1)
1185 : : assertAtExpr(nodep);
1186 : : if (m_vup->prelim()) {
1187 : : iterateCheckSizedSelf(nodep, "Bit select", nodep->bitp(), SELF, BOTH);
1188 : : userIterateAndNext(nodep->fromp(), WidthVP{SELF, BOTH}.p());
1189 : : //
1190 : : int frommsb;
1191 : : int fromlsb;
1192 : : const AstNodeDType* const fromDtp = nodep->fromp()->dtypep()->skipRefp();
1193 : : if (const AstUnpackArrayDType* const adtypep = VN_CAST(fromDtp, UnpackArrayDType)) {
1194 : : frommsb = adtypep->hi();
1195 : : fromlsb = adtypep->lo();
1196 : : if (fromlsb > frommsb) std::swap(frommsb, fromlsb);
1197 : : // However, if the lsb<0 we may go negative, so need more bits!
1198 : : if (fromlsb < 0) frommsb += -fromlsb;
1199 : : nodep->dtypeFrom(adtypep->subDTypep()); // Need to strip off array reference
1200 : : } else {
1201 : : // Note PackArrayDType doesn't use an ArraySel but a normal Sel.
1202 : : UINFO(1, " Related dtype: " << fromDtp);
1203 : : nodep->v3fatalSrc("Array reference exceeds dimension of array");
1204 : : frommsb = fromlsb = 0;
1205 : : }
1206 : : const int selwidth = V3Number::log2b(frommsb + 1 - 1) + 1; // Width to address a bit
1207 : : AstNodeDType* const selwidthDTypep
1208 : : = nodep->findLogicDType(selwidth, selwidth, nodep->bitp()->dtypep()->numeric());
1209 : : if (widthBad(nodep->bitp(), selwidthDTypep) && nodep->bitp()->width() != 32) {
1210 : : nodep->v3widthWarn(selwidth, nodep->bitp()->width(),
1211 : : "Bit extraction of array["
1212 : : << frommsb << ":" << fromlsb << "] requires " << selwidth
1213 : : << " bit index, not " << nodep->bitp()->width()
1214 : : << (nodep->bitp()->width() != nodep->bitp()->widthMin()
1215 : : ? " or " + cvtToStr(nodep->bitp()->widthMin())
1216 : : : "")
1217 : : << " bits.");
1218 : : if (!nodep->fileline()->warnIsOff(V3ErrorCode::WIDTH)) {
1219 : : UINFO(1, " Related node: " << nodep);
1220 : : UINFO(1, " Related dtype: " << nodep->dtypep());
1221 : : }
1222 : : }
1223 : : if (!m_doGenerate) {
1224 : : // Must check bounds before adding a select that truncates the bound
1225 : : // Note we've already subtracted off LSB
1226 : : if (VN_IS(nodep->bitp(), Const)
1227 : : && (VN_AS(nodep->bitp(), Const)->toSInt() > (frommsb - fromlsb)
1228 : : || VN_AS(nodep->bitp(), Const)->toSInt() < 0)) {
1229 : : // Suppress in dead/parameterized template modules
1230 : : if (!(m_modep && (m_modep->dead() || m_modep->parameterizedTemplate()))) {
1231 : : nodep->v3warn(SELRANGE,
1232 : : "Selection index out of range: "
1233 : : << (VN_AS(nodep->bitp(), Const)->toSInt() + fromlsb)
1234 : : << " outside " << frommsb << ":" << fromlsb);
1235 : : }
1236 : : }
1237 : : widthCheckSized(nodep, "Extract Range", nodep->bitp(), selwidthDTypep, EXTEND_EXP,
1238 : : false /*NOWARN*/);
1239 : : }
1240 : : }
1241 : : }
1242 : :
1243 : : void visit(AstAssocSel* nodep) override {
1244 : : // Signed/Real: Output type based on array-declared type; binary operator
1245 : : assertAtExpr(nodep);
1246 : : if (m_vup->prelim()) {
1247 : : const AstNodeDType* const fromDtp = nodep->fromp()->dtypep()->skipRefp();
1248 : : const AstAssocArrayDType* const adtypep = VN_CAST(fromDtp, AssocArrayDType);
1249 : : if (!adtypep) {
1250 : : UINFO(1, " Related dtype: " << fromDtp);
1251 : : nodep->v3fatalSrc("Associative array reference is not to associative array");
1252 : : }
1253 : : iterateCheckTyped(nodep, "Associative select", nodep->bitp(), adtypep->keyDTypep(),
1254 : : BOTH);
1255 : : nodep->dtypeFrom(adtypep->subDTypep());
1256 : : }
1257 : : }
1258 : :
1259 : : void visit(AstAlias* nodep) override {
1260 : : if (!nodep->didWidthAndSet()) {
1261 : : userIterateAndNext(nodep->itemsp(), WidthVP{SELF, BOTH}.p());
1262 : : }
1263 : :
1264 : : const auto checkIfExprOk = [this](const AstNodeExpr* const exprp) {
1265 : : if (VN_IS(exprp, VarXRef)) {
1266 : : exprp->v3error("Hierarchical reference used for net alias (IEEE 1800-2023 10.11)");
1267 : : return false;
1268 : : }
1269 : :
1270 : : const AstVarRef* const varRefp = VN_CAST(exprp, VarRef);
1271 : : if (!varRefp) {
1272 : : exprp->v3warn(
1273 : : E_UNSUPPORTED,
1274 : : "Unsupported: Operand of alias statement is not a variable reference");
1275 : : return false;
1276 : : }
1277 : : AstVar* const varp = varRefp->varp();
1278 : : if (!varp->isNet()) {
1279 : : exprp->v3error("Only nets are allowed in alias (IEEE 1800-2023 10.11): "
1280 : : << varp->prettyNameQ());
1281 : : return false;
1282 : : }
1283 : : if (m_aliasedVars.find(varp) != m_aliasedVars.end()) {
1284 : : varRefp->v3error("Alias is specified more than once (IEEE 1800-2023 10.11): "
1285 : : << varp->prettyNameQ());
1286 : : return false;
1287 : : } else {
1288 : : m_aliasedVars.insert(varp);
1289 : : }
1290 : : return true;
1291 : : };
1292 : :
1293 : : checkIfExprOk(nodep->itemsp());
1294 : : AstNodeDType* const firstItemDtypep = nodep->itemsp()->dtypep();
1295 : : for (AstNode* itemp = nodep->itemsp()->nextp(); itemp; itemp = itemp->nextp()) {
1296 : : checkIfExprOk(VN_AS(itemp, NodeExpr));
1297 : : if (!firstItemDtypep->similarDType(itemp->dtypep())) {
1298 : : itemp->v3error("Incompatible data types of nets used for net alias. First operand "
1299 : : "has the type "
1300 : : << firstItemDtypep->prettyDTypeNameQ() << ", other has "
1301 : : << itemp->dtypep()->prettyDTypeNameQ());
1302 : : }
1303 : : }
1304 : : }
1305 : :
1306 : : void visit(AstWildcardSel* nodep) override {
1307 : : // Signed/Real: Output type based on array-declared type; binary operator
1308 : : assertAtExpr(nodep);
1309 : : if (m_vup->prelim()) {
1310 : : const AstNodeDType* const fromDtp = nodep->fromp()->dtypep()->skipRefp();
1311 : : const AstWildcardArrayDType* const adtypep = VN_CAST(fromDtp, WildcardArrayDType);
1312 : : if (!adtypep) {
1313 : : UINFO(1, " Related dtype: " << fromDtp);
1314 : : nodep->v3fatalSrc("Wildcard array reference is not to wildcard array");
1315 : : }
1316 : : const AstBasicDType* const basicp = nodep->bitp()->dtypep()->skipRefp()->basicp();
1317 : : if (!basicp
1318 : : || (basicp->keyword() != VBasicDTypeKwd::STRING
1319 : : && !basicp->keyword().isIntNumeric())) {
1320 : : nodep->v3error("Wildcard index must be integral (IEEE 1800-2023 7.8.1)");
1321 : : }
1322 : : iterateCheckTyped(nodep, "Wildcard associative select", nodep->bitp(),
1323 : : adtypep->findStringDType(), BOTH);
1324 : : nodep->dtypeFrom(adtypep->subDTypep());
1325 : : }
1326 : : }
1327 : :
1328 : : void visit(AstSliceSel* nodep) override {
1329 : : // Always creates as output an unpacked array
1330 : : assertAtExpr(nodep);
1331 : : if (m_vup->prelim()) {
1332 : : userIterateAndNext(nodep->fromp(), WidthVP{SELF, BOTH}.p());
1333 : : //
1334 : : // Array indices are always constant
1335 : : const AstNodeDType* const fromDtp = nodep->fromp()->dtypep()->skipRefp();
1336 : : const AstUnpackArrayDType* const adtypep = VN_CAST(fromDtp, UnpackArrayDType);
1337 : : if (!adtypep) {
1338 : : UINFO(1, " Related dtype: " << fromDtp);
1339 : : nodep->v3fatalSrc("Packed array reference exceeds dimension of array");
1340 : : }
1341 : : // Build new array Dtype based on the original's base type, but with new bounds
1342 : : AstNodeDType* const newDtp
1343 : : = new AstUnpackArrayDType{nodep->fileline(), adtypep->subDTypep(),
1344 : : new AstRange{nodep->fileline(), nodep->declRange()}};
1345 : : v3Global.rootp()->typeTablep()->addTypesp(newDtp);
1346 : : nodep->dtypeFrom(newDtp);
1347 : :
1348 : : if (!m_doGenerate) {
1349 : : // Must check bounds before adding a select that truncates the bound
1350 : : // Note we've already subtracted off LSB
1351 : : const int subtracted = adtypep->declRange().lo();
1352 : : // Add subtracted value to get the original range
1353 : : const VNumRange declRange{nodep->declRange().hi() + subtracted,
1354 : : nodep->declRange().lo() + subtracted,
1355 : : nodep->declRange().ascending()};
1356 : : if ((declRange.hi() > adtypep->declRange().hi())
1357 : : || declRange.lo() < adtypep->declRange().lo()) {
1358 : : // Other simulators warn too
1359 : : nodep->v3error("Slice selection index '" << declRange << "'"
1360 : : << " outside data type's '"
1361 : : << adtypep->declRange() << "'");
1362 : : } else if ((declRange.ascending() != adtypep->declRange().ascending())
1363 : : && declRange.hi() != declRange.lo()) {
1364 : : nodep->v3error("Slice selection '"
1365 : : << declRange << "'"
1366 : : << " has reversed range order versus data type's '"
1367 : : << adtypep->declRange() << "'");
1368 : : }
1369 : : }
1370 : : }
1371 : : }
1372 : :
1373 : : void visit(AstSelBit* nodep) override {
1374 : : // Just a quick check as after V3Param these nodes instead are AstSel's
1375 : : userIterateAndNext(nodep->fromp(), WidthVP{CONTEXT_DET, PRELIM}.p()); // FINAL in AstSel
1376 : : userIterateAndNext(nodep->bitp(), WidthVP{CONTEXT_DET, PRELIM}.p()); // FINAL in AstSel
1377 : : userIterateAndNext(nodep->thsp(), WidthVP{CONTEXT_DET, PRELIM}.p()); // FINAL in AstSel
1378 : : userIterateAndNext(nodep->attrp(), WidthVP{SELF, BOTH}.p());
1379 : : AstNode* const selp = V3Width::widthSelNoIterEdit(nodep);
1380 : : if (selp != nodep) {
1381 : : VL_DANGLING(nodep);
1382 : : userIterate(selp, m_vup);
1383 : : return;
1384 : : }
1385 : : nodep->v3fatalSrc("AstSelBit should disappear after widthSel");
1386 : : }
1387 : : void visit(AstSelExtract* nodep) override {
1388 : : // Just a quick check as after V3Param these nodes instead are AstSel's
1389 : : userIterateAndNext(nodep->fromp(), WidthVP{CONTEXT_DET, PRELIM}.p()); // FINAL in AstSel
1390 : : userIterateAndNext(nodep->leftp(), WidthVP{CONTEXT_DET, PRELIM}.p()); // FINAL in AstSel
1391 : : userIterateAndNext(nodep->rightp(), WidthVP{CONTEXT_DET, PRELIM}.p()); // FINAL in AstSel
1392 : : userIterateAndNext(nodep->attrp(), WidthVP{SELF, BOTH}.p());
1393 : : AstNode* const selp = V3Width::widthSelNoIterEdit(nodep);
1394 : : if (selp != nodep) {
1395 : : nodep = nullptr;
1396 : : userIterate(selp, m_vup);
1397 : : return;
1398 : : }
1399 : : nodep->v3fatalSrc("AstSelExtract should disappear after widthSel");
1400 : : }
1401 : : void visit(AstSelPlus* nodep) override {
1402 : : userIterateAndNext(nodep->fromp(), WidthVP{CONTEXT_DET, PRELIM}.p()); // FINAL in AstSel
1403 : : userIterateAndNext(nodep->bitp(), WidthVP{CONTEXT_DET, PRELIM}.p()); // FINAL in AstSel
1404 : : userIterateAndNext(nodep->widthp(), WidthVP{CONTEXT_DET, PRELIM}.p()); // FINAL in AstSel
1405 : : userIterateAndNext(nodep->attrp(), WidthVP{SELF, BOTH}.p());
1406 : : AstNode* const selp = V3Width::widthSelNoIterEdit(nodep);
1407 : : if (selp != nodep) {
1408 : : nodep = nullptr;
1409 : : userIterate(selp, m_vup);
1410 : : return;
1411 : : }
1412 : : nodep->v3fatalSrc("AstSelPlus should disappear after widthSel");
1413 : : }
1414 : : void visit(AstSelMinus* nodep) override {
1415 : : userIterateAndNext(nodep->fromp(), WidthVP{CONTEXT_DET, PRELIM}.p()); // FINAL in AstSel
1416 : : userIterateAndNext(nodep->bitp(), WidthVP{CONTEXT_DET, PRELIM}.p()); // FINAL in AstSel
1417 : : userIterateAndNext(nodep->widthp(), WidthVP{CONTEXT_DET, PRELIM}.p()); // FINAL in AstSel
1418 : : userIterateAndNext(nodep->attrp(), WidthVP{SELF, BOTH}.p());
1419 : : AstNode* const selp = V3Width::widthSelNoIterEdit(nodep);
1420 : : if (selp != nodep) {
1421 : : nodep = nullptr;
1422 : : userIterate(selp, m_vup);
1423 : : return;
1424 : : }
1425 : : nodep->v3fatalSrc("AstSelMinus should disappear after widthSel");
1426 : : }
1427 : :
1428 : : void visit(AstExtend* nodep) override {
1429 : : // Typically created by this process, so we know width from here down is correct.
1430 : : // Exception is extend added by V3WidthSel - those need iteration
1431 : : if (nodep->didWidthAndSet()) return;
1432 : : userIterateAndNext(nodep->lhsp(), WidthVP{SELF, BOTH}.p());
1433 : : }
1434 : : void visit(AstExtendS* nodep) override {
1435 : : // Typically created by this process, so we know width from here down is correct.
1436 : : // Exception is extend added by V3WidthSel - those need iteration
1437 : : if (nodep->didWidthAndSet()) return;
1438 : : userIterateAndNext(nodep->lhsp(), WidthVP{SELF, BOTH}.p());
1439 : : }
1440 : : void visit(AstConst* nodep) override {
1441 : : // The node got setup with the signed/real state of the node.
1442 : : // However a later operation may have changed the node->signed w/o changing
1443 : : // the number's sign. So we don't: nodep->dtypeChgSigned(nodep->num().isSigned());
1444 : : if (nodep->didWidthAndSet()) return;
1445 : : if (m_vup && m_vup->prelim()) {
1446 : : if (VN_IS(nodep->dtypep()->skipRefToEnump(), EnumDType)) {
1447 : : // Assume this constant was properly casted earlier
1448 : : // (Otherwise it couldn't have an enum data type)
1449 : : } else if (nodep->num().isString()) {
1450 : : nodep->dtypeSetString();
1451 : : } else if (nodep->num().sized()) {
1452 : : nodep->dtypeChgWidth(nodep->num().width(), nodep->num().width());
1453 : : } else {
1454 : : nodep->dtypeChgWidth(nodep->num().width(), nodep->num().widthToFit());
1455 : : }
1456 : : }
1457 : : // We don't size the constant until we commit the widths, as need parameters
1458 : : // to remain unsized, and numbers to remain unsized to avoid backp() warnings
1459 : : }
1460 : : void visit(AstEmptyQueue* nodep) override {
1461 : : nodep->dtypeSetEmptyQueue();
1462 : : if (!VN_IS(nodep->backp(), Assign) && !VN_IS(nodep->backp(), Var)) {
1463 : : nodep->v3warn(E_UNSUPPORTED,
1464 : : "Unsupported/Illegal: empty queue ('{}') in this context");
1465 : : }
1466 : : }
1467 : : void visit(AstFell* nodep) override {
1468 : : assertAtExpr(nodep);
1469 : : if (m_vup->prelim()) {
1470 : : iterateCheckSizedSelf(nodep, "LHS", nodep->exprp(), SELF, BOTH);
1471 : : userIterate(nodep->sentreep(), nullptr);
1472 : : nodep->dtypeSetBit();
1473 : : }
1474 : : }
1475 : : void visit(AstFalling* nodep) override {
1476 : : assertAtExpr(nodep);
1477 : : if (m_vup->prelim()) {
1478 : : iterateCheckSizedSelf(nodep, "LHS", nodep->exprp(), SELF, BOTH);
1479 : : nodep->dtypeSetBit();
1480 : : }
1481 : : }
1482 : : void visit(AstFuture* nodep) override {
1483 : : assertAtExpr(nodep);
1484 : : if (m_vup->prelim()) {
1485 : : iterateCheckSizedSelf(nodep, "LHS", nodep->exprp(), SELF, BOTH);
1486 : : userIterate(nodep->sentreep(), nullptr);
1487 : : nodep->dtypeFrom(nodep->exprp());
1488 : : }
1489 : : }
1490 : : void visit(AstPast* nodep) override {
1491 : : assertAtExpr(nodep);
1492 : : if (m_vup->prelim()) {
1493 : : iterateCheckSizedSelf(nodep, "LHS", nodep->exprp(), SELF, BOTH);
1494 : : nodep->dtypeFrom(nodep->exprp());
1495 : : if (nodep->ticksp()) {
1496 : : iterateCheckSizedSelf(nodep, "Ticks", nodep->ticksp(), SELF, BOTH);
1497 : : V3Const::constifyParamsEdit(nodep->ticksp()); // ticksp may change
1498 : : const AstConst* const constp = VN_CAST(nodep->ticksp(), Const);
1499 : : if (!constp) {
1500 : : nodep->v3error("$past tick value must be constant (IEEE 1800-2023 16.9.3)");
1501 : : nodep->ticksp()->unlinkFrBack()->deleteTree();
1502 : : } else if (constp->toSInt() < 1) {
1503 : : constp->v3error("$past tick value must be >= 1 (IEEE 1800-2023 16.9.3)");
1504 : : nodep->ticksp()->unlinkFrBack()->deleteTree();
1505 : : } else {
1506 : : if (constp->toSInt() > 10) {
1507 : : constp->v3warn(TICKCOUNT, "$past tick value of "
1508 : : << constp->toSInt()
1509 : : << " may have a large performance cost");
1510 : : }
1511 : : }
1512 : : }
1513 : : userIterate(nodep->sentreep(), nullptr);
1514 : : }
1515 : : }
1516 : : void visit(AstSConsRep* nodep) override {
1517 : : // IEEE 1800-2023 16.9.2 -- consecutive repetition [*N], [*N:M], [+], [*]
1518 : : assertAtExpr(nodep);
1519 : : if (m_vup->prelim()) {
1520 : : userIterateAndNext(nodep->exprp(), WidthVP{SELF, BOTH}.p());
1521 : : userIterateAndNext(nodep->countp(), WidthVP{SELF, BOTH}.p());
1522 : : V3Const::constifyParamsEdit(nodep->countp());
1523 : : const AstConst* const minConstp = VN_CAST(nodep->countp(), Const);
1524 : : if (!minConstp) {
1525 : : nodep->v3error("Consecutive repetition count must be constant expression"
1526 : : " (IEEE 1800-2023 16.9.2)");
1527 : : } else if (!nodep->unbounded() && !nodep->maxCountp() && minConstp->toSInt() < 1) {
1528 : : nodep->v3warn(E_UNSUPPORTED, "Unsupported: [*0] consecutive repetition");
1529 : : }
1530 : : if (nodep->maxCountp()) {
1531 : : userIterateAndNext(nodep->maxCountp(), WidthVP{SELF, BOTH}.p());
1532 : : V3Const::constifyParamsEdit(nodep->maxCountp());
1533 : : const AstConst* const maxConstp = VN_CAST(nodep->maxCountp(), Const);
1534 : : if (!maxConstp) {
1535 : : nodep->v3error("Consecutive repetition max count must be constant"
1536 : : " expression (IEEE 1800-2023 16.9.2)");
1537 : : } else if (minConstp && maxConstp->toSInt() < minConstp->toSInt()) {
1538 : : nodep->v3error("Consecutive repetition max count must be >= min count"
1539 : : " (IEEE 1800-2023 16.9.2)");
1540 : : } else if (maxConstp->toSInt() < 1) {
1541 : : nodep->v3warn(E_UNSUPPORTED, "Unsupported: [*N:0] consecutive repetition"
1542 : : " with zero max count");
1543 : : }
1544 : : }
1545 : : nodep->dtypeSetBit();
1546 : : }
1547 : : }
1548 : : void visit(AstPropAlways* nodep) override {
1549 : : // IEEE 1800-2023 16.12.11
1550 : : assertAtExpr(nodep);
1551 : : if (m_vup->prelim()) {
1552 : : userIterateAndNext(nodep->propp(), WidthVP{SELF, BOTH}.p());
1553 : : if (!VN_IS(nodep->loBoundp(), Unbounded)) {
1554 : : userIterateAndNext(nodep->loBoundp(), WidthVP{SELF, BOTH}.p());
1555 : : V3Const::constifyParamsEdit(nodep->loBoundp());
1556 : : }
1557 : : if (!VN_IS(nodep->hiBoundp(), Unbounded)) {
1558 : : userIterateAndNext(nodep->hiBoundp(), WidthVP{SELF, BOTH}.p());
1559 : : V3Const::constifyParamsEdit(nodep->hiBoundp());
1560 : : }
1561 : : const bool loUnbounded = VN_IS(nodep->loBoundp(), Unbounded);
1562 : : const bool hiUnbounded = VN_IS(nodep->hiBoundp(), Unbounded);
1563 : : if (loUnbounded || hiUnbounded) {
1564 : : if (nodep->isStrong()) {
1565 : : nodep->v3error("s_always range must be bounded (IEEE 1800-2023 16.12.11)");
1566 : : } else {
1567 : : nodep->v3warn(E_UNSUPPORTED,
1568 : : "Unsupported: unbounded always range (always [m:$])");
1569 : : }
1570 : : nodep->dtypeSetBit();
1571 : : return;
1572 : : }
1573 : : const AstConst* const loConstp = VN_CAST(nodep->loBoundp(), Const);
1574 : : const AstConst* const hiConstp = VN_CAST(nodep->hiBoundp(), Const);
1575 : : if (!loConstp) {
1576 : : nodep->v3error("always range low bound must be a constant expression"
1577 : : " (IEEE 1800-2023 16.12.11)");
1578 : : }
1579 : : if (!hiConstp) {
1580 : : nodep->v3error("always range high bound must be a constant expression"
1581 : : " (IEEE 1800-2023 16.12.11)");
1582 : : }
1583 : : if (loConstp && loConstp->toSInt() < 0) {
1584 : : nodep->v3error("always range low bound must be non-negative"
1585 : : " (IEEE 1800-2023 16.12.11)");
1586 : : }
1587 : : if (loConstp && hiConstp && hiConstp->toSInt() < loConstp->toSInt()) {
1588 : : nodep->v3error("always range high bound must be >= low bound"
1589 : : " (IEEE 1800-2023 16.12.11)");
1590 : : }
1591 : : bool hasPropertyOp = nodep->propp()->isMultiCycleSva();
1592 : : if (!hasPropertyOp) {
1593 : : nodep->propp()->foreach([&](const AstNode* np) {
1594 : : if (VN_IS(np, Implication) || VN_IS(np, Until) || VN_IS(np, PropSpec)) {
1595 : : hasPropertyOp = true;
1596 : : }
1597 : : });
1598 : : }
1599 : : if (hasPropertyOp) {
1600 : : nodep->v3warn(E_UNSUPPORTED,
1601 : : "Unsupported: property/sequence operator inside bounded"
1602 : : " always (IEEE 1800-2023 16.12.11)");
1603 : : }
1604 : : nodep->dtypeSetBit();
1605 : : }
1606 : : }
1607 : : void visit(AstRising* nodep) override {
1608 : : assertAtExpr(nodep);
1609 : : if (m_vup->prelim()) {
1610 : : iterateCheckSizedSelf(nodep, "LHS", nodep->exprp(), SELF, BOTH);
1611 : : nodep->dtypeSetBit();
1612 : : }
1613 : : }
1614 : : void visit(AstRose* nodep) override {
1615 : : assertAtExpr(nodep);
1616 : : if (m_vup->prelim()) {
1617 : : iterateCheckSizedSelf(nodep, "LHS", nodep->exprp(), SELF, BOTH);
1618 : : userIterate(nodep->sentreep(), nullptr);
1619 : : nodep->dtypeSetBit();
1620 : : }
1621 : : }
1622 : :
1623 : : void visit(AstSampled* nodep) override {
1624 : : assertAtExpr(nodep);
1625 : : if (m_vup->prelim()) {
1626 : : iterateCheckSizedSelf(nodep, "LHS", nodep->exprp(), SELF, BOTH);
1627 : : nodep->dtypeFrom(nodep->exprp());
1628 : : }
1629 : : }
1630 : :
1631 : : void visit(AstSetuphold* nodep) override {
1632 : : FileLine* const flp = nodep->fileline();
1633 : : AstAssignW* newp = nullptr;
1634 : : if (nodep->delrefp()) {
1635 : : newp = convertSetupholdToAssign(flp, nodep->refevp(), nodep->delrefp());
1636 : : }
1637 : : if (nodep->deldatap()) {
1638 : : if (!newp) {
1639 : : newp = convertSetupholdToAssign(flp, nodep->dataevp(), nodep->deldatap());
1640 : : } else {
1641 : : newp->addNextHere(
1642 : : convertSetupholdToAssign(flp, nodep->dataevp(), nodep->deldatap()));
1643 : : }
1644 : : }
1645 : : if (!newp) {
1646 : : pushDeletep(nodep->unlinkFrBack());
1647 : : return;
1648 : : }
1649 : : nodep->replaceWith(new AstAlways{newp});
1650 : : VL_DO_DANGLING(pushDeletep(nodep), nodep);
1651 : : }
1652 : :
1653 : : AstAssignW* convertSetupholdToAssign(FileLine* const flp, AstNodeExpr* const evp,
1654 : : AstNodeExpr* const delp) {
1655 : : AstNodeExpr* const lhsp = delp->cloneTreePure(false);
1656 : : AstNodeExpr* const rhsp = evp->cloneTreePure(false);
1657 : : UASSERT_OBJ(VN_IS(lhsp, NodeVarRef) || VN_IS(lhsp, NodePreSel), lhsp,
1658 : : "Incorrect reference in a timing check");
1659 : : if (AstNodeVarRef* varRefp = VN_CAST(lhsp, NodeVarRef)) {
1660 : : if (varRefp->varp()->direction() == VDirection::INPUT) {
1661 : : VL_DO_DANGLING(pushDeletep(lhsp), lhsp);
1662 : : VL_DO_DANGLING(pushDeletep(rhsp), rhsp);
1663 : : return nullptr;
1664 : : }
1665 : : varRefp->access(VAccess::WRITE);
1666 : : }
1667 : : if (AstNodePreSel* selp = VN_CAST(lhsp, NodePreSel)) {
1668 : : if (AstNodeVarRef* varRefp = VN_CAST(selp->fromp(), NodeVarRef)) {
1669 : : if (varRefp->varp()->direction() == VDirection::INPUT) {
1670 : : VL_DO_DANGLING(pushDeletep(lhsp), lhsp);
1671 : : VL_DO_DANGLING(pushDeletep(rhsp), rhsp);
1672 : : return nullptr;
1673 : : }
1674 : : varRefp->access(VAccess::WRITE);
1675 : : }
1676 : : }
1677 : : return new AstAssignW{flp, lhsp, rhsp};
1678 : : }
1679 : :
1680 : : void visit(AstStable* nodep) override {
1681 : : assertAtExpr(nodep);
1682 : : if (m_vup->prelim()) {
1683 : : iterateCheckSizedSelf(nodep, "LHS", nodep->exprp(), SELF, BOTH);
1684 : : userIterate(nodep->sentreep(), nullptr);
1685 : : nodep->dtypeSetBit();
1686 : : }
1687 : : }
1688 : : void visit(AstSteady* nodep) override {
1689 : : assertAtExpr(nodep);
1690 : : if (m_vup->prelim()) {
1691 : : iterateCheckSizedSelf(nodep, "LHS", nodep->exprp(), SELF, BOTH);
1692 : : nodep->dtypeSetBit();
1693 : : }
1694 : : }
1695 : :
1696 : : void visit(AstStmtExpr* nodep) override {
1697 : : assertAtStatement(nodep);
1698 : : userIterateAndNext(nodep->exprp(), WidthVP{SELF, BOTH}.p());
1699 : : }
1700 : :
1701 : : void visit(AstImplication* nodep) override {
1702 : : m_seqUnsupp = nodep;
1703 : : assertAtExpr(nodep);
1704 : : if (m_vup->prelim()) {
1705 : : iterateCheckBool(nodep, "LHS", nodep->lhsp(), BOTH);
1706 : : iterateCheckBool(nodep, "RHS", nodep->rhsp(), BOTH);
1707 : : nodep->dtypeSetBit();
1708 : : }
1709 : : }
1710 : :
1711 : : void visit(AstUntil* nodep) override {
1712 : : assertAtExpr(nodep);
1713 : : if (m_vup->prelim()) {
1714 : : iterateCheckBool(nodep, "LHS", nodep->lhsp(), BOTH);
1715 : : iterateCheckBool(nodep, "RHS", nodep->rhsp(), BOTH);
1716 : : nodep->dtypeSetBit();
1717 : : }
1718 : : }
1719 : :
1720 : : void visit(AstRand* nodep) override {
1721 : : assertAtExpr(nodep);
1722 : : if (m_vup->prelim()) {
1723 : : if (nodep->urandom()) {
1724 : : nodep->dtypeSetUInt32(); // Says the spec
1725 : : } else {
1726 : : nodep->dtypeSetSigned32(); // Says the spec
1727 : : }
1728 : : if (nodep->seedp()) iterateCheckSigned32(nodep, "seed", nodep->seedp(), BOTH);
1729 : : }
1730 : : }
1731 : 15 : void visit(AstSEventually* nodep) override {
1732 [ + + ][ + + ]: 15 : if (v3Global.opt.timing().isSetFalse() || !v3Global.opt.timing().isSetTrue()) {
[ + + ]
1733 : 4 : nodep->v3warn(E_NOTIMING, "s_eventually requires --timing");
1734 : 4 : nodep->replaceWith(new AstConst{nodep->fileline(), AstConst::WidthedValue{}, 1, 0});
1735 : 4 : VL_DO_DANGLING(nodep->deleteTree(), nodep);
1736 : 4 : return;
1737 : : }
1738 [ + - ]: 11 : if (m_vup->prelim()) {
1739 : 11 : iterateCheckBool(nodep, "exprp", nodep->exprp(), BOTH);
1740 : 11 : nodep->dtypeSetBit();
1741 : : }
1742 : : }
1743 : : void visit(AstSGotoRep* nodep) override {
1744 : : assertAtExpr(nodep);
1745 : : if (m_vup->prelim()) {
1746 : : iterateCheckBool(nodep, "exprp", nodep->exprp(), BOTH);
1747 : : userIterateAndNext(nodep->countp(), WidthVP{SELF, BOTH}.p());
1748 : : nodep->dtypeSetBit();
1749 : : }
1750 : : }
1751 : : void visit(AstSNonConsRep* nodep) override {
1752 : : assertAtExpr(nodep);
1753 : : if (m_vup->prelim()) {
1754 : : iterateCheckBool(nodep, "exprp", nodep->exprp(), BOTH);
1755 : : userIterateAndNext(nodep->countp(), WidthVP{SELF, BOTH}.p());
1756 : : nodep->dtypeSetBit();
1757 : : }
1758 : : }
1759 : : void visit(AstSThroughout* nodep) override {
1760 : : m_hasSExpr = true;
1761 : : assertAtExpr(nodep);
1762 : : if (m_vup->prelim()) {
1763 : : // lhsp is a boolean expression, not a sequence -- clear m_underSExpr temporarily
1764 : : VL_RESTORER(m_underSExpr);
1765 : : m_underSExpr = false;
1766 : : iterateCheckBool(nodep, "lhsp", nodep->lhsp(), BOTH);
1767 : : m_underSExpr = true;
1768 : : iterate(nodep->rhsp());
1769 : : nodep->dtypeSetBit();
1770 : : }
1771 : : }
1772 : : void visit(AstSExpr* nodep) override {
1773 : : VL_RESTORER(m_underSExpr);
1774 : : m_underSExpr = true;
1775 : : m_hasSExpr = true;
1776 : : assertAtExpr(nodep);
1777 : : if (m_vup->prelim()) {
1778 : : if (nodep->preExprp()) {
1779 : : iterateCheckBool(nodep, "preExprp", nodep->preExprp(), BOTH);
1780 : : }
1781 : : iterate(nodep->delayp());
1782 : : iterateCheckBool(nodep, "exprp", nodep->exprp(), BOTH);
1783 : : nodep->dtypeSetBit();
1784 : : }
1785 : : }
1786 : : void visit(AstURandomRange* nodep) override {
1787 : : assertAtExpr(nodep);
1788 : : if (m_vup->prelim()) {
1789 : : nodep->dtypeSetUInt32(); // Says the spec
1790 : : AstNodeDType* const expDTypep = nodep->findUInt32DType();
1791 : : userIterateAndNext(nodep->lhsp(), WidthVP{CONTEXT_DET, PRELIM}.p());
1792 : : userIterateAndNext(nodep->rhsp(), WidthVP{CONTEXT_DET, PRELIM}.p());
1793 : : iterateCheck(nodep, "LHS", nodep->lhsp(), SELF, FINAL, expDTypep, EXTEND_EXP);
1794 : : iterateCheck(nodep, "RHS", nodep->rhsp(), SELF, FINAL, expDTypep, EXTEND_EXP);
1795 : : }
1796 : : }
1797 : : void visit(AstUnbounded* nodep) override {
1798 : : nodep->dtypeSetSigned32(); // Used in int context
1799 : : if (VN_IS(nodep->backp(), IsUnbounded)) return; // Ok, leave
1800 : : if (VN_IS(nodep->backp(), BracketArrayDType)) return; // Ok, leave
1801 : : if (VN_IS(nodep->backp(), InsideRange)) return; // Ok, leave
1802 : : if (const auto* const varp = VN_CAST(nodep->backp(), Var)) {
1803 : : if (varp->isParam()) return; // Ok, leave
1804 : : }
1805 : : AstNode* backp = nodep->backp();
1806 : : if (VN_IS(backp, Sub)) backp = backp->backp();
1807 : : if (const auto* const selp = VN_CAST(backp, SelExtract)) {
1808 : : if (VN_IS(selp->fromp()->dtypep()->skipRefp(), QueueDType)) return;
1809 : : }
1810 : : if (const auto* const selp = VN_CAST(backp, SelBit)) {
1811 : : if (VN_IS(selp->fromp()->dtypep()->skipRefp(), QueueDType)) return;
1812 : : }
1813 : : // queue_slice[#:$] and queue_bitsel[$] etc handled in V3WidthSel
1814 : : nodep->v3warn(E_UNSUPPORTED,
1815 : : "Unsupported: Unbounded ('$') outside of queue or string operations");
1816 : : }
1817 : : void visit(AstInferredDisable* nodep) override {
1818 : : assertAtExpr(nodep);
1819 : : if (m_vup->prelim()) nodep->dtypeSetBit();
1820 : : }
1821 : : void visit(AstIsUnbounded* nodep) override {
1822 : : assertAtExpr(nodep);
1823 : : if (m_vup->prelim()) {
1824 : : userIterateAndNext(nodep->lhsp(), WidthVP{SELF, BOTH}.p());
1825 : : nodep->dtypeSetBit();
1826 : : }
1827 : : }
1828 : : void visit(AstCExpr* nodep) override {
1829 : : // Inserted by V3Width only so we know has been resolved
1830 : : userIterateChildren(nodep, WidthVP{SELF, BOTH}.p());
1831 : : }
1832 : : void visit(AstCExprUser* nodep) override {
1833 : : // Give it the size the user wants.
1834 : : if (m_vup && m_vup->prelim()) {
1835 : : nodep->dtypeSetLogicUnsized(32, 1, VSigning::UNSIGNED); // We don't care
1836 : : // All arguments seek their natural sizes
1837 : : userIterateChildren(nodep, WidthVP{SELF, BOTH}.p());
1838 : : }
1839 : : if (m_vup->final()) {
1840 : : AstNodeDType* const expDTypep = m_vup->dtypeOverridep(nodep->dtypep());
1841 : : nodep->dtypep(expDTypep); // Assume user knows the rules; go with the flow
1842 : : if (nodep->width() > 64) {
1843 : : nodep->v3warn(E_UNSUPPORTED, "Unsupported: $c can't generate wider than 64 bits");
1844 : : }
1845 : : }
1846 : : }
1847 : : void visit(AstCLog2* nodep) override {
1848 : : assertAtExpr(nodep);
1849 : : if (m_vup->prelim()) iterateCheckSizedSelf(nodep, "LHS", nodep->lhsp(), SELF, BOTH);
1850 : : }
1851 : : void visit(AstCgOptionAssign* nodep) override {
1852 : : // We report COVERIGN on the whole covergroup; if get more fine-grained add this
1853 : : // nodep->v3warn(COVERIGN, "Ignoring unsupported: coverage option");
1854 : : VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
1855 : : }
1856 : : void visit(AstPow* nodep) override {
1857 : : // Pow is special, output sign only depends on LHS sign, but
1858 : : // function result depends on both signs
1859 : : // RHS is self-determined (IEEE)
1860 : : // Real if either side is real (as with AstAdd)
1861 : : assertAtExpr(nodep);
1862 : : if (m_vup->prelim()) {
1863 : : userIterateAndNext(nodep->lhsp(), WidthVP{CONTEXT_DET, PRELIM}.p());
1864 : : userIterateAndNext(nodep->rhsp(), WidthVP{CONTEXT_DET, PRELIM}.p());
1865 : : if (nodep->lhsp()->isDouble() || nodep->rhsp()->isDouble()) {
1866 : : spliceCvtD(nodep->lhsp());
1867 : : spliceCvtD(nodep->rhsp());
1868 : : VL_DO_DANGLING(replaceWithDVersion(nodep), nodep);
1869 : : return;
1870 : : }
1871 : :
1872 : : checkCvtUS(nodep->lhsp(), false);
1873 : : iterateCheckSizedSelf(nodep, "RHS", nodep->rhsp(), SELF, BOTH);
1874 : : nodep->dtypeFrom(nodep->lhsp());
1875 : : }
1876 : :
1877 : : if (m_vup->final()) {
1878 : : AstNodeDType* const expDTypep = m_vup->dtypeOverridep(nodep->dtypep());
1879 : : nodep->dtypep(expDTypep);
1880 : : // rhs already finalized in iterate_shift_prelim
1881 : : iterateCheck(nodep, "LHS", nodep->lhsp(), SELF, FINAL, nodep->dtypep(), EXTEND_EXP);
1882 : : AstNode* newp = nullptr; // No change
1883 : : if (nodep->lhsp()->isSigned() && nodep->rhsp()->isSigned()) {
1884 : : newp = new AstPowSS{nodep->fileline(), nodep->lhsp()->unlinkFrBack(),
1885 : : nodep->rhsp()->unlinkFrBack()};
1886 : : } else if (nodep->lhsp()->isSigned() && !nodep->rhsp()->isSigned()) {
1887 : : newp = new AstPowSU{nodep->fileline(), nodep->lhsp()->unlinkFrBack(),
1888 : : nodep->rhsp()->unlinkFrBack()};
1889 : : } else if (!nodep->lhsp()->isSigned() && nodep->rhsp()->isSigned()) {
1890 : : newp = new AstPowUS{nodep->fileline(), nodep->lhsp()->unlinkFrBack(),
1891 : : nodep->rhsp()->unlinkFrBack()};
1892 : : }
1893 : : if (newp) {
1894 : : newp->dtypeFrom(nodep);
1895 : : UINFO(9, "powOld " << nodep);
1896 : : UINFO(9, "powNew " << newp);
1897 : : nodep->replaceWith(newp);
1898 : : VL_DO_DANGLING(pushDeletep(nodep), nodep);
1899 : : }
1900 : : }
1901 : : }
1902 : : void visit(AstPowSU* nodep) override {
1903 : : // POWSU/SS/US only created here, dtype already determined, so
1904 : : // nothing to do in this function
1905 : : userIterateAndNext(nodep->lhsp(), WidthVP{SELF, BOTH}.p());
1906 : : userIterateAndNext(nodep->rhsp(), WidthVP{SELF, BOTH}.p());
1907 : : }
1908 : : void visit(AstPowSS* nodep) override {
1909 : : // POWSU/SS/US only created here, dtype already determined, so
1910 : : // nothing to do in this function
1911 : : userIterateAndNext(nodep->lhsp(), WidthVP{SELF, BOTH}.p());
1912 : : userIterateAndNext(nodep->rhsp(), WidthVP{SELF, BOTH}.p());
1913 : : }
1914 : : void visit(AstPowUS* nodep) override {
1915 : : // POWSU/SS/US only created here, dtype already determined, so
1916 : : // nothing to do in this function
1917 : : userIterateAndNext(nodep->lhsp(), WidthVP{SELF, BOTH}.p());
1918 : : userIterateAndNext(nodep->rhsp(), WidthVP{SELF, BOTH}.p());
1919 : : }
1920 : : void visit(AstCountBits* nodep) override {
1921 : : assertAtExpr(nodep);
1922 : : if (m_vup->prelim()) {
1923 : : iterateCheckSizedSelf(nodep, "LHS", nodep->lhsp(), SELF, BOTH);
1924 : : iterateCheckSizedSelf(nodep, "RHS", nodep->rhsp(), SELF, BOTH);
1925 : : iterateCheckSizedSelf(nodep, "THS", nodep->thsp(), SELF, BOTH);
1926 : : iterateCheckSizedSelf(nodep, "FHS", nodep->fhsp(), SELF, BOTH);
1927 : : // For widthMin, if a 32 bit number, we need a 6 bit number as we need to return '32'.
1928 : : const int widthMin = V3Number::log2b(nodep->lhsp()->width()) + 1;
1929 : : nodep->dtypeSetLogicUnsized(32, widthMin, VSigning::SIGNED);
1930 : : }
1931 : : }
1932 : : void visit(AstCountOnes* nodep) override {
1933 : : if (m_vup->prelim()) {
1934 : : iterateCheckSizedSelf(nodep, "LHS", nodep->lhsp(), SELF, BOTH);
1935 : : // For widthMin, if a 32 bit number, we need a 6 bit number as we need to return '32'.
1936 : : const int widthMin = V3Number::log2b(nodep->lhsp()->width()) + 1;
1937 : : nodep->dtypeSetLogicUnsized(32, widthMin, VSigning::SIGNED);
1938 : : }
1939 : : }
1940 : : void visit(AstCvtPackString* nodep) override {
1941 : : if (nodep->didWidthAndSet()) return;
1942 : : // Opaque returns, so arbitrary
1943 : : userIterateAndNext(nodep->lhsp(), WidthVP{SELF, BOTH}.p());
1944 : : // Type set in constructor
1945 : : }
1946 : : void visit(AstCvtPackedToArray* nodep) override {
1947 : : if (nodep->didWidthAndSet()) return;
1948 : : // Opaque returns, so arbitrary
1949 : : userIterateAndNext(nodep->fromp(), WidthVP{SELF, BOTH}.p());
1950 : : // Type set in constructor
1951 : : }
1952 : : void visit(AstCvtArrayToArray* nodep) override {
1953 : : if (nodep->didWidthAndSet()) return;
1954 : : // Opaque returns, so arbitrary
1955 : : userIterateAndNext(nodep->fromp(), WidthVP{SELF, BOTH}.p());
1956 : : // Type set in constructor
1957 : : }
1958 : : void visit(AstCvtArrayToPacked* nodep) override {
1959 : : if (nodep->didWidthAndSet()) return;
1960 : : // Opaque returns, so arbitrary
1961 : : userIterateAndNext(nodep->fromp(), WidthVP{SELF, BOTH}.p());
1962 : : // Type set in constructor
1963 : : }
1964 : : void visit(AstCvtUnpackedToQueue* nodep) override {
1965 : : if (nodep->didWidthAndSet()) return;
1966 : : // Opaque returns, so arbitrary
1967 : : userIterateAndNext(nodep->fromp(), WidthVP{SELF, BOTH}.p());
1968 : : // Type set in constructor
1969 : : }
1970 : : void visit(AstTimeImport* nodep) override {
1971 : : // LHS is a real number in seconds
1972 : : // Need to round to time units and precision
1973 : : userIterateAndNext(nodep->lhsp(), WidthVP{SELF, BOTH}.p());
1974 : : const AstConst* const constp = VN_CAST(nodep->lhsp(), Const);
1975 : : UASSERT_OBJ(constp && constp->isDouble(), nodep, "Times should be doubles");
1976 : : UASSERT_OBJ(!nodep->timeunit().isNone(), nodep, "$time import no units");
1977 : : const double timePrescale = constp->num().toDouble();
1978 : : UASSERT_OBJ(!v3Global.rootp()->timeprecision().isNone(), nodep, "Never set precision?");
1979 : : const double time = timePrescale / nodep->timeunit().multiplier();
1980 : : // IEEE claims you should round to time precision here, but no simulator seems to do this
1981 : : AstConst* const newp = new AstConst{nodep->fileline(), AstConst::RealDouble{}, time};
1982 : : nodep->replaceWith(newp);
1983 : : VL_DO_DANGLING(nodep->deleteTree(), nodep);
1984 : : }
1985 : : void visit(AstEventControl* nodep) override {
1986 : : if (!m_underFork && VN_IS(m_ftaskp, Func)) {
1987 : : nodep->v3error("Event controls are not legal in functions. Suggest use a task "
1988 : : "(IEEE 1800-2023 13.4.4)");
1989 : : VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
1990 : : return;
1991 : : }
1992 : : if (nodep->fileline()->timingOn()) {
1993 : : if (v3Global.opt.timing().isSetTrue()) {
1994 : : iterateChildren(nodep);
1995 : : return;
1996 : : } else if (v3Global.opt.timing().isSetFalse()) {
1997 : : nodep->v3warn(E_NOTIMING,
1998 : : "Event control statement in this location requires --timing\n"
1999 : : << nodep->warnMore()
2000 : : << "... With --no-timing, suggest have one event control "
2001 : : << "statement per procedure, at the top of the procedure");
2002 : : } else {
2003 : : nodep->v3warn(E_NEEDTIMINGOPT, "Use --timing or --no-timing to specify how "
2004 : : "event controls should be handled");
2005 : : }
2006 : : }
2007 : : if (nodep->stmtsp()) nodep->addNextHere(nodep->stmtsp()->unlinkFrBack());
2008 : : VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
2009 : : }
2010 : : void visit(AstAttrOf* nodep) override {
2011 : : VL_RESTORER(m_attrp);
2012 : : m_attrp = nodep;
2013 : : userIterateAndNext(nodep->fromp(), WidthVP{SELF, BOTH}.p());
2014 : : if (nodep->dimp()) userIterateAndNext(nodep->dimp(), WidthVP{SELF, BOTH}.p());
2015 : : // Don't iterate children, don't want to lose VarRef.
2016 : : switch (nodep->attrType()) {
2017 : : case VAttrType::DIM_DIMENSIONS:
2018 : : case VAttrType::DIM_UNPK_DIMENSIONS: {
2019 : : AstNodeDType* const dtypep = fromDTypep(nodep->fromp());
2020 : : UASSERT_OBJ(dtypep, nodep, "Unsized expression");
2021 : : const std::pair<uint32_t, uint32_t> dim = dtypep->dimensions(true);
2022 : : const int val
2023 : : = (nodep->attrType() == VAttrType::DIM_UNPK_DIMENSIONS ? dim.second
2024 : : : (dim.first + dim.second));
2025 : : nodep->replaceWith(new AstConst(nodep->fileline(), AstConst::Signed32{}, val));
2026 : : VL_DO_DANGLING(nodep->deleteTree(), nodep);
2027 : : break;
2028 : : }
2029 : : case VAttrType::DIM_BITS_OR_NUMBER: {
2030 : : // If dtype, compute DIM_BITS, else take expression as a number to use
2031 : : if (VN_IS(nodep->fromp(), NodeExpr)) {
2032 : : nodep->replaceWith(nodep->fromp()->unlinkFrBack());
2033 : : VL_DO_DANGLING(pushDeletep(nodep), nodep);
2034 : : } else {
2035 : : AstNode* newp = new AstAttrOf{nodep->fileline(), VAttrType::DIM_BITS,
2036 : : nodep->fromp()->unlinkFrBack()};
2037 : : nodep->replaceWith(newp);
2038 : : VL_DO_DANGLING(pushDeletep(nodep), nodep);
2039 : : userIterateAndNext(newp, WidthVP{SELF, BOTH}.p()); // Convert AttrOf
2040 : : }
2041 : : return;
2042 : : }
2043 : : case VAttrType::DIM_BITS:
2044 : : case VAttrType::DIM_HIGH:
2045 : : case VAttrType::DIM_INCREMENT:
2046 : : case VAttrType::DIM_LEFT:
2047 : : case VAttrType::DIM_LOW:
2048 : : case VAttrType::DIM_RIGHT:
2049 : : case VAttrType::DIM_SIZE: {
2050 : : AstNodeDType* const dtypep = fromDTypep(nodep->fromp());
2051 : : UASSERT_OBJ(dtypep, nodep, "Unsized expression");
2052 : : if (VN_IS(dtypep, QueueDType) || VN_IS(dtypep, DynArrayDType)) {
2053 : : switch (nodep->attrType()) {
2054 : : case VAttrType::DIM_SIZE: {
2055 : : AstNodeExpr* const fromp = VN_AS(nodep->fromp()->unlinkFrBack(), NodeExpr);
2056 : : AstNode* const newp
2057 : : = new AstCMethodHard{nodep->fileline(), fromp, VCMethod::DYN_SIZE};
2058 : : newp->dtypeSetSigned32();
2059 : : newp->didWidth(true);
2060 : : newp->protect(false);
2061 : : nodep->replaceWith(newp);
2062 : : VL_DO_DANGLING(nodep->deleteTree(), nodep);
2063 : : break;
2064 : : }
2065 : : case VAttrType::DIM_LEFT:
2066 : : case VAttrType::DIM_LOW: {
2067 : : AstNode* const newp = new AstConst(nodep->fileline(), AstConst::Signed32{}, 0);
2068 : : nodep->replaceWith(newp);
2069 : : VL_DO_DANGLING(nodep->deleteTree(), nodep);
2070 : : break;
2071 : : }
2072 : : case VAttrType::DIM_RIGHT:
2073 : : case VAttrType::DIM_HIGH: {
2074 : : AstNodeExpr* const fromp = VN_AS(nodep->fromp()->unlinkFrBack(), NodeExpr);
2075 : : AstNodeExpr* const sizep
2076 : : = new AstCMethodHard{nodep->fileline(), fromp, VCMethod::DYN_SIZE};
2077 : : sizep->dtypeSetSigned32();
2078 : : sizep->didWidth(true);
2079 : : sizep->protect(false);
2080 : : AstNode* const newp
2081 : : = new AstSub{nodep->fileline(), sizep,
2082 : : new AstConst(nodep->fileline(), AstConst::Signed32{}, 1)};
2083 : : nodep->replaceWith(newp);
2084 : : VL_DO_DANGLING(nodep->deleteTree(), nodep);
2085 : : break;
2086 : : }
2087 : : case VAttrType::DIM_INCREMENT: {
2088 : : AstNodeExpr* const newp
2089 : : = new AstConst(nodep->fileline(), AstConst::Signed32{}, -1);
2090 : : nodep->replaceWith(newp);
2091 : : VL_DO_DANGLING(nodep->deleteTree(), nodep);
2092 : : break;
2093 : : }
2094 : : case VAttrType::DIM_BITS: {
2095 : : if (VN_IS(dtypep, DynArrayDType)) {
2096 : : nodep->v3warn(E_UNSUPPORTED, "Unsupported: $bits for dynamic array");
2097 : : } else {
2098 : : nodep->v3warn(E_UNSUPPORTED, "Unsupported: $bits for queue");
2099 : : }
2100 : : break;
2101 : : }
2102 : : default: nodep->v3fatalSrc("Unhandled attribute type");
2103 : : }
2104 : : } else {
2105 : : const std::pair<uint32_t, uint32_t> dimpair = dtypep->skipRefp()->dimensions(true);
2106 : : const uint32_t msbdim = dimpair.first + dimpair.second;
2107 : : if (!nodep->dimp() || msbdim < 1) {
2108 : : if (VN_IS(dtypep, BasicDType) && dtypep->basicp()->isString()) {
2109 : : // IEEE undocumented but $bits(string) must give length(string) * 8
2110 : : AstNodeExpr* const fromp = VN_AS(nodep->fromp()->unlinkFrBack(), NodeExpr);
2111 : : AstNode* const newp = new AstShiftL{
2112 : : nodep->fileline(), new AstLenN{nodep->fileline(), fromp},
2113 : : new AstConst{nodep->fileline(), 3}, // * 8
2114 : : 32};
2115 : : nodep->replaceWith(newp);
2116 : : VL_DO_DANGLING(pushDeletep(nodep), nodep);
2117 : : } else {
2118 : : AstNodeDType* const baseDTypep = dtypep->skipRefp();
2119 : : UASSERT_OBJ(baseDTypep, nodep, "Unsized expression");
2120 : : const int dim = 1;
2121 : : AstConst* const newp = dimensionValue(nodep->fileline(), baseDTypep,
2122 : : nodep->attrType(), dim);
2123 : : nodep->replaceWith(newp);
2124 : : VL_DO_DANGLING(nodep->deleteTree(), nodep);
2125 : : }
2126 : : } else if (VN_IS(nodep->dimp(), Const)) {
2127 : : const int dim = VN_AS(nodep->dimp(), Const)->toSInt();
2128 : : AstConst* const newp
2129 : : = dimensionValue(nodep->fileline(), dtypep, nodep->attrType(), dim);
2130 : : nodep->replaceWith(newp);
2131 : : VL_DO_DANGLING(nodep->deleteTree(), nodep);
2132 : : } else { // Need a runtime lookup table. Yuk.
2133 : : UASSERT_OBJ(nodep->fromp() && dtypep, nodep, "Unsized expression");
2134 : : AstVar* const varp = dimensionVarp(dtypep, nodep->attrType(), msbdim);
2135 : : AstNodeExpr* const dimp = nodep->dimp()->unlinkFrBack();
2136 : : AstNodeExpr* const newp
2137 : : = new AstArraySel{nodep->fileline(), newVarRefDollarUnit(varp), dimp};
2138 : : nodep->replaceWith(newp);
2139 : : VL_DO_DANGLING(nodep->deleteTree(), nodep);
2140 : : }
2141 : : }
2142 : : break;
2143 : : }
2144 : : case VAttrType::FUNC_ARG_PROTO:
2145 : : // Created by V3LinkDot only to check that the prototype was correct when we got here
2146 : : // Checked at bottom of visit(AstNodeFTask)
2147 : : // V3WidthCommit::visit(AstAttrOf) will delete nodep
2148 : : break;
2149 : : case VAttrType::FUNC_RETURN_PROTO: {
2150 : : // Created by V3LinkDot only to check that the prototype was correct when we got here
2151 : : UASSERT_OBJ(m_ftaskp, nodep, "FUNC attr not under function");
2152 : : AstNodeDType* const protoDtp = nodep->fromp()->dtypep();
2153 : : AstNodeDType* const declDtp = [&]() {
2154 : : if (m_ftaskp->fvarp()) return m_ftaskp->fvarp()->dtypep();
2155 : : AstNodeDType* const voidp = new AstVoidDType{m_ftaskp->fileline()};
2156 : : pushDeletep(voidp);
2157 : : return voidp;
2158 : : }();
2159 : : if (!similarDTypeRecurse(protoDtp, declDtp)) {
2160 : : protoDtp->v3warn(
2161 : : PROTOTYPEMIS,
2162 : : "In prototype for "
2163 : : << m_ftaskp->prettyNameQ()
2164 : : << ", return data type does not match out-of-block"
2165 : : " declaration data-type (IEEE 1800-2023 8.24)\n"
2166 : : << protoDtp->warnMore()
2167 : : << "... Prototype data type: " << protoDtp->prettyDTypeNameQ() << '\n'
2168 : : << protoDtp->warnMore()
2169 : : << "... Declaration data type: " << declDtp->prettyDTypeNameQ() << '\n'
2170 : : << protoDtp->warnContextPrimary() << '\n'
2171 : : << declDtp->warnOther() << "... Location of out-of-block declaration\n"
2172 : : << declDtp->warnContextSecondary());
2173 : : }
2174 : : // V3WidthCommit::visit(AstAttrOf) will delete nodep
2175 : : break;
2176 : : }
2177 : : case VAttrType::TYPENAME: {
2178 : : UASSERT_OBJ(nodep->fromp(), nodep, "Unprovided expression");
2179 : : const string result = nodep->fromp()->dtypep()->prettyDTypeName(true);
2180 : : UINFO(9, "typename '" << result << "' from " << nodep->fromp()->dtypep());
2181 : : AstNode* const newp = new AstConst{nodep->fileline(), AstConst::String{}, result};
2182 : : nodep->replaceWith(newp);
2183 : : VL_DO_DANGLING(nodep->deleteTree(), nodep);
2184 : : break;
2185 : : }
2186 : : case VAttrType::TYPEID:
2187 : : if (AstNodeDType* dtypep = VN_CAST(nodep->fromp(), NodeDType)) {
2188 : : nodep->dtypep(dtypep);
2189 : : } else {
2190 : : nodep->dtypep(VN_AS(nodep->fromp(), NodeExpr)->dtypep());
2191 : : }
2192 : : break;
2193 : : case VAttrType::VAR_BASE:
2194 : : // Soon to be handled in V3LinkWidth SEL generation, under attrp() and newSubLsbOf
2195 : : break;
2196 : : default: {
2197 : : // Everything else resolved earlier
2198 : : nodep->dtypeSetLogicUnsized(32, 1, VSigning::UNSIGNED); // Approximation, unsized 32
2199 : : UINFO(1, "Missing ATTR type case node: " << nodep);
2200 : : nodep->v3fatalSrc("Missing ATTR type case");
2201 : : break;
2202 : : }
2203 : : }
2204 : : }
2205 : : void visit(AstPull* nodep) override {
2206 : : // May have select underneath, let seek natural size
2207 : : userIterateChildren(nodep, WidthVP{SELF, BOTH}.p());
2208 : : }
2209 : : void visit(AstText* nodep) override {
2210 : : // Only used in CStmts which don't care....
2211 : : }
2212 : :
2213 : : void visit(AstCellArrayRef* nodep) override {
2214 : : if (nodep->didWidthAndSet()) return;
2215 : : userIterateAndNext(nodep->selp(), WidthVP{SELF, PRELIM}.p());
2216 : : userIterateAndNext(nodep->selp(), WidthVP{SELF, FINAL}.p());
2217 : : nodep->dtypeSetVoid(); // placeholder; this node shouldnt survive beyond linking
2218 : : nodep->didWidth(true);
2219 : : }
2220 : :
2221 : : void visit(AstCellRef* nodep) override {
2222 : : if (nodep->didWidthAndSet()) return;
2223 : :
2224 : : if (AstNodeExpr* const cellExprp = VN_CAST(nodep->cellp(), NodeExpr)) {
2225 : : userIterateAndNext(cellExprp, WidthVP{SELF, PRELIM}.p());
2226 : : userIterateAndNext(cellExprp, WidthVP{SELF, FINAL}.p());
2227 : : }
2228 : :
2229 : : if (AstNodeExpr* const exprp = VN_CAST(nodep->exprp(), NodeExpr)) {
2230 : : userIterateAndNext(exprp, WidthVP{SELF, PRELIM}.p());
2231 : : nodep->dtypeFrom(exprp);
2232 : : userIterateAndNext(exprp, WidthVP{SELF, FINAL}.p());
2233 : : } else {
2234 : : nodep->v3fatalSrc("CellRef exprp is not a NodeExpr: " // LCOV_EXCL_LINE
2235 : : << nodep->exprp()->prettyTypeName());
2236 : : }
2237 : :
2238 : : nodep->didWidth(true);
2239 : : }
2240 : :
2241 : : // DTYPES
2242 : : void visit(AstNodeArrayDType* nodep) override {
2243 : : if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed
2244 : :
2245 : : if (nodep->subDTypep() == nodep->basicp()) { // Innermost dimension
2246 : : AstBasicDType* const basicp = nodep->basicp();
2247 : : // If basic dtype is LOGIC_IMPLICIT, it is actually 1 bit LOGIC
2248 : : if (basicp->implicit()) {
2249 : : UASSERT_OBJ(basicp->width() <= 1, basicp,
2250 : : "must be 1 bit but actually " << basicp->width() << " bits");
2251 : : AstBasicDType* const newp = new AstBasicDType{
2252 : : basicp->fileline(), VBasicDTypeKwd::LOGIC, basicp->numeric()};
2253 : : newp->widthForce(1, 1);
2254 : : basicp->replaceWith(newp);
2255 : : VL_DO_DANGLING(pushDeletep(basicp), basicp);
2256 : : }
2257 : : }
2258 : : if (!m_underPackedArray) m_hasNamedType = false; // Outermost dimension
2259 : : VL_RESTORER(m_hasNamedType);
2260 : : VL_RESTORER(m_underPackedArray);
2261 : : if (VN_IS(nodep, PackArrayDType)) m_underPackedArray = true;
2262 : : // Iterate into subDTypep() to resolve that type and update pointer.
2263 : : nodep->refDTypep(iterateEditMoveDTypep(nodep, nodep->subDTypep()));
2264 : :
2265 : : // Cleanup array size
2266 : : userIterateAndNext(nodep->rangep(), WidthVP{SELF, BOTH}.p());
2267 : : nodep->dtypep(nodep); // The array itself, not subDtype
2268 : : if (AstUnpackArrayDType* const adtypep = VN_CAST(nodep, UnpackArrayDType)) {
2269 : : // Historically array elements have width of the ref type not the full array
2270 : : nodep->widthFromSub(nodep->subDTypep());
2271 : : if (nodep->subDTypep()->skipRefp()->isCompound()) adtypep->isCompound(true);
2272 : : } else {
2273 : : const int width = nodep->subDTypep()->width() * nodep->rangep()->elementsConst();
2274 : : nodep->widthForce(width, width);
2275 : : if (!VL_RESTORER_PREV(m_underPackedArray)) { // Outermost dimension
2276 : : // IEEE 1800-2023 7.4.1 "Packed arrays" says
2277 : : // If a packed array is declared as signed,
2278 : : // then the array viewed as a single vector shall be signed.
2279 : : if (!m_hasNamedType && nodep->basicp()->isSigned()) {
2280 : : nodep->numeric(VSigning::fromBool(true));
2281 : : }
2282 : : }
2283 : : }
2284 : : UINFO(4, "dtWidthed " << nodep);
2285 : : }
2286 : : void visit(AstAssocArrayDType* nodep) override {
2287 : : if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed
2288 : : // Iterate into subDTypep() to resolve that type and update pointer.
2289 : : nodep->refDTypep(iterateEditMoveDTypep(nodep, nodep->subDTypep()));
2290 : : nodep->keyDTypep(iterateEditMoveDTypep(nodep, nodep->keyDTypep()));
2291 : : nodep->dtypep(nodep); // The array itself, not subDtype
2292 : : UINFO(4, "dtWidthed " << nodep);
2293 : : }
2294 : : void visit(AstBracketArrayDType* nodep) override {
2295 : : // Type inserted only because parser didn't know elementsp() type
2296 : : // Resolve elementsp's type
2297 : : userIterateChildren(nodep, WidthVP{SELF, BOTH}.p());
2298 : : // We must edit when dtype still under normal nodes and before type table
2299 : : // See notes in iterateEditMoveDTypep
2300 : : AstNodeDType* const childp = nodep->childDTypep();
2301 : : childp->unlinkFrBack();
2302 : : AstNode* const elementsp = nodep->elementsp()->unlinkFrBack();
2303 : : AstNode* newp;
2304 : : if (VN_IS(elementsp, Unbounded)) {
2305 : : newp = new AstQueueDType{nodep->fileline(), VFlagChildDType{}, childp, nullptr};
2306 : : VL_DO_DANGLING(elementsp->deleteTree(), elementsp);
2307 : : } else if (AstNodeDType* const keyp = VN_CAST(elementsp, NodeDType)) {
2308 : : newp = new AstAssocArrayDType{nodep->fileline(), VFlagChildDType{}, childp, keyp};
2309 : : } else {
2310 : : // The subtract in the range may confuse users; as the array
2311 : : // size is self determined there's no reason to warn about widths
2312 : : FileLine* const elementsNewFl = elementsp->fileline();
2313 : : elementsNewFl->warnOff(V3ErrorCode::WIDTHEXPAND, true);
2314 : : // Must be expression that is constant, but we'll determine that later
2315 : : newp = new AstUnpackArrayDType{
2316 : : nodep->fileline(), VFlagChildDType{}, childp,
2317 : : new AstRange{nodep->fileline(), new AstConst(elementsp->fileline(), 0),
2318 : : new AstSub{elementsNewFl, VN_AS(elementsp, NodeExpr),
2319 : : new AstConst(elementsNewFl, 1)},
2320 : : true}};
2321 : : }
2322 : : nodep->replaceWith(newp);
2323 : : VL_DO_DANGLING(nodep->deleteTree(), nodep);
2324 : : // Normally parent's iteration would cover this, but we might have entered by a specific
2325 : : // visit
2326 : : VL_DO_DANGLING(userIterate(newp, nullptr), newp);
2327 : : }
2328 : : void visit(AstConstraintRefDType* nodep) override {
2329 : : if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed
2330 : : nodep->dtypep(nodep);
2331 : : }
2332 : : void visit(AstDynArrayDType* nodep) override {
2333 : : if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed
2334 : : // Iterate into subDTypep() to resolve that type and update pointer.
2335 : : nodep->refDTypep(iterateEditMoveDTypep(nodep, nodep->subDTypep()));
2336 : : nodep->dtypep(nodep); // The array itself, not subDtype
2337 : : UINFO(4, "dtWidthed " << nodep);
2338 : : }
2339 : : void visit(AstQueueDType* nodep) override {
2340 : : if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed
2341 : : // Iterate into subDTypep() to resolve that type and update pointer.
2342 : : nodep->refDTypep(iterateEditMoveDTypep(nodep, nodep->subDTypep()));
2343 : : nodep->dtypep(nodep); // The array itself, not subDtype
2344 : : userIterateAndNext(nodep->boundp(), WidthVP{SELF, BOTH}.p());
2345 : : if (VN_IS(nodep->boundp(), Unbounded)) {
2346 : : nodep->boundp()->unlinkFrBack()->deleteTree(); // nullptr will represent unbounded
2347 : : }
2348 : : UINFO(4, "dtWidthed " << nodep);
2349 : : }
2350 : : void visit(AstVoidDType* nodep) override {
2351 : : if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed
2352 : : nodep->dtypep(nodep);
2353 : : UINFO(4, "dtWidthed " << nodep);
2354 : : }
2355 : : void visit(AstUnsizedArrayDType* nodep) override {
2356 : : if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed
2357 : : // Iterate into subDTypep() to resolve that type and update pointer.
2358 : : nodep->refDTypep(iterateEditMoveDTypep(nodep, nodep->subDTypep()));
2359 : : // Cleanup array size
2360 : : nodep->dtypep(nodep); // The array itself, not subDtype
2361 : : UINFO(4, "dtWidthed " << nodep);
2362 : : }
2363 : : void visit(AstWildcardArrayDType* 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 : : // Cleanup array size
2368 : : nodep->dtypep(nodep); // The array itself, not subDtype
2369 : : UINFO(4, "dtWidthed " << nodep);
2370 : : }
2371 : : void visit(AstBasicDType* nodep) override {
2372 : : if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed
2373 : : if (nodep->generic()) return; // Already perfect
2374 : : if (nodep->rangep()) {
2375 : : userIterateAndNext(nodep->rangep(), WidthVP{SELF, BOTH}.p());
2376 : : // Because this DType has a unique child range, we know it's not
2377 : : // pointed at by other nodes unless they are referencing this type.
2378 : : // Furthermore the width() calculation would return identical
2379 : : // values. Therefore we can directly replace the width
2380 : : nodep->widthForce(nodep->rangep()->elementsConst(), nodep->rangep()->elementsConst());
2381 : : } else if (nodep->isRanged()) {
2382 : : nodep->widthForce(nodep->nrange().elements(), nodep->nrange().elements());
2383 : : } else if (nodep->implicit()) {
2384 : : // Parameters may notice implicitness and change to different dtype
2385 : : nodep->widthForce(1, 1);
2386 : : }
2387 : : // else width in node is correct; it was set based on keyword().width()
2388 : : // at construction time. Ditto signed, so "unsigned byte" etc works right.
2389 : : nodep->cvtRangeConst();
2390 : : // TODO: If BasicDType now looks like a generic type, we can convert to a real generic
2391 : : // dtype Instead for now doing this in V3WidthCommit
2392 : : UINFO(4, "dtWidthed " << nodep);
2393 : : }
2394 : : void visit(AstConstDType* nodep) override {
2395 : : if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed
2396 : : // Iterate into subDTypep() to resolve that type and update pointer.
2397 : : nodep->refDTypep(iterateEditMoveDTypep(nodep, nodep->subDTypep()));
2398 : : userIterateChildren(nodep, nullptr);
2399 : : nodep->dtypep(nodep); // Should already be set, but be clear it's not the subDType
2400 : : nodep->widthFromSub(nodep->subDTypep());
2401 : : UINFO(4, "dtWidthed " << nodep);
2402 : : }
2403 : : void visit(AstRefDType* nodep) override {
2404 : : m_hasNamedType = m_underPackedArray;
2405 : : if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed
2406 : : nodep->doingWidth(true);
2407 : : if (nodep->typeofp()) { // type(typeofp_expression)
2408 : : if (AstNodeDType* typeofDtp = VN_CAST(nodep->typeofp(), NodeDType)) {
2409 : : // It's directly a type, e.g. "type(int)"
2410 : : typeofDtp = iterateEditMoveDTypep(nodep, typeofDtp); // Changes typeofp
2411 : : nodep->refDTypep(typeofDtp);
2412 : : } else {
2413 : : // Type comes from expression's type, e.g. "type(variable)"
2414 : : userIterateAndNext(nodep->typeofp(), WidthVP{SELF, BOTH}.p());
2415 : : AstNode* const typeofp = nodep->typeofp();
2416 : : nodep->refDTypep(typeofp->dtypep());
2417 : : VL_DO_DANGLING(typeofp->unlinkFrBack()->deleteTree(), typeofp);
2418 : : }
2419 : : // We had to use AstRefDType for this construct as pointers to this type
2420 : : // in type table are still correct (which they wouldn't be if we replaced the node)
2421 : : }
2422 : : userIterateChildren(nodep, nullptr);
2423 : : if (nodep->subDTypep()) {
2424 : : // Normally iterateEditMoveDTypep iterate would work, but the refs are under
2425 : : // the TypeDef which will upset iterateEditMoveDTypep as it can't find it under
2426 : : // this node's childDTypep
2427 : : userIterate(nodep->subDTypep(), nullptr);
2428 : : nodep->refDTypep(iterateEditMoveDTypep(nodep, nodep->subDTypep()));
2429 : : // Widths are resolved, but special iterate to check for recursion
2430 : : userIterate(nodep->subDTypep(), nullptr);
2431 : : }
2432 : : // Effectively nodep->dtypeFrom(nodep->dtypeSkipRefp());
2433 : : // But might be recursive, so instead manually recurse into the referenced type
2434 : : if (!nodep->subDTypep()) {
2435 : : // Defer unlinked RefDTypes in parameterized template modules (or types
2436 : : // with no owning module, e.g. moved to TypeTable) until specialization
2437 : : // resolves them.
2438 : : const bool inTemplateModule = !m_modep || m_modep->parameterizedTemplate();
2439 : : if (inTemplateModule) {
2440 : : nodep->doingWidth(false);
2441 : : return;
2442 : : }
2443 : : }
2444 : : UASSERT_OBJ(nodep->subDTypep(), nodep, "Unlinked");
2445 : : nodep->dtypeFrom(nodep->subDTypep());
2446 : : nodep->widthFromSub(nodep->subDTypep());
2447 : : UINFO(4, "dtWidthed " << nodep);
2448 : : // No nodep->typedefp(nullptr) for now; V3WidthCommit needs to check accesses
2449 : : nodep->doingWidth(false);
2450 : : }
2451 : : void visit(AstTypedef* nodep) override {
2452 : : if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed
2453 : : if (auto* const refp = checkRefToTypedefRecurse(nodep, nodep)) {
2454 : : nodep->v3error("Typedef has self-reference: " << nodep->prettyNameQ() << '\n'
2455 : : << nodep->warnContextPrimary() << '\n'
2456 : : << refp->warnOther()
2457 : : << "... Location of reference\n"
2458 : : << refp->warnContextSecondary());
2459 : : // May cause internal error but avoids infinite loop on dump
2460 : : refp->typedefp(nullptr);
2461 : : VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
2462 : : return;
2463 : : }
2464 : : nodep->dtypep(iterateEditMoveDTypep(nodep, nodep->subDTypep()));
2465 : : userIterateChildren(nodep, nullptr);
2466 : : }
2467 : : void visit(AstParamTypeDType* nodep) override {
2468 : : if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed
2469 : :
2470 : : nodep->dtypep(iterateEditMoveDTypep(nodep, nodep->subDTypep()));
2471 : : userIterateChildren(nodep, nullptr);
2472 : : nodep->widthFromSub(nodep->subDTypep());
2473 : : // Clear childDTypep after dtypep is set to satisfy V3Broken invariant.
2474 : : // The child dtype has been moved to the type table by iterateEditMoveDTypep.
2475 : : if (nodep->dtypep() && nodep->childDTypep()) { nodep->childDTypep(nullptr); }
2476 : : }
2477 : : void visit(AstRequireDType* nodep) override {
2478 : : userIterateAndNext(nodep->lhsp(), WidthVP{SELF, BOTH}.p());
2479 : : if (AstNodeDType* const dtp = VN_CAST(nodep->lhsp(), NodeDType)) {
2480 : : nodep->replaceWith(dtp->unlinkFrBack());
2481 : : } else {
2482 : : if (nodep->lhsp())
2483 : : nodep->lhsp()->v3error("Expected data type, not a "
2484 : : << nodep->lhsp()->prettyTypeName());
2485 : : nodep->replaceWith(new AstVoidDType{nodep->fileline()});
2486 : : }
2487 : : VL_DO_DANGLING(pushDeletep(nodep), nodep);
2488 : : }
2489 : : void visit(AstCastDynamic* nodep) override {
2490 : : nodep->dtypeChgWidthSigned(32, 1, VSigning::SIGNED); // Spec says integer return
2491 : : userIterateChildren(nodep, WidthVP{SELF, BOTH}.p());
2492 : : AstNodeDType* const toDtp = nodep->top()->dtypep()->skipRefToEnump();
2493 : : AstNodeDType* const fromDtp = nodep->fromp()->dtypep()->skipRefToEnump();
2494 : : FileLine* const fl = nodep->fileline();
2495 : : const auto castable = AstNode::computeCastable(toDtp, fromDtp, nodep->fromp());
2496 : : AstNode* newp;
2497 : : if (castable == VCastable::DYNAMIC_CLASS) {
2498 : : // Keep in place, will compute at runtime
2499 : : return;
2500 : : } else if (castable == VCastable::ENUM_EXPLICIT || castable == VCastable::ENUM_IMPLICIT) {
2501 : : // TODO is from is a constant we could simplify, though normal constant
2502 : : // elimination should do much the same
2503 : : // Form: "( ((v > size) ? false : enum_valid[v[N:0]])
2504 : : // ? ExprStmt(ExprAssign(out, Cast(v, type)), 1) : 0)"
2505 : : AstEnumDType* const enumDtp = VN_AS(toDtp, EnumDType);
2506 : : UASSERT_OBJ(enumDtp, nodep, "$cast determined as enum, but not enum type");
2507 : : AstNodeExpr* const testp = enumTestValid(nodep->fromp(), enumDtp);
2508 : : FileLine* const fl_novalue = new FileLine{fl};
2509 : : fl_novalue->warnOff(V3ErrorCode::ENUMVALUE, true);
2510 : : newp = new AstCond{
2511 : : fl, testp,
2512 : : new AstExprStmt{fl,
2513 : : new AstAssign{fl_novalue, nodep->top()->unlinkFrBack(),
2514 : : nodep->fromp()->unlinkFrBack()},
2515 : : new AstConst{fl, AstConst::Signed32{}, 1}},
2516 : : new AstConst{fl, AstConst::Signed32{}, 0}};
2517 : : } else if (castable == VCastable::SAMEISH || castable == VCastable::COMPATIBLE) {
2518 : : nodep->v3warn(CASTCONST, "$cast will always return one as "
2519 : : << toDtp->prettyDTypeNameQ()
2520 : : << " is always castable from "
2521 : : << fromDtp->prettyDTypeNameQ() << '\n'
2522 : : << nodep->warnMore() << "... Suggest static cast");
2523 : : newp = new AstExprStmt{
2524 : : fl,
2525 : : new AstAssign{fl, nodep->top()->unlinkFrBack(),
2526 : : new AstCast{fl, nodep->fromp()->unlinkFrBack(), toDtp}},
2527 : : new AstConst{fl, AstConst::Signed32{}, 1}};
2528 : : } else if (castable == VCastable::INCOMPATIBLE) {
2529 : : newp = new AstConst{fl, 0};
2530 : : nodep->v3warn(CASTCONST, "$cast will always return zero as "
2531 : : << toDtp->prettyDTypeNameQ() << " is not castable from "
2532 : : << fromDtp->prettyDTypeNameQ());
2533 : : } else {
2534 : : newp = new AstConst{fl, 0};
2535 : : nodep->v3warn(E_UNSUPPORTED, "Unsupported: $cast to "
2536 : : << toDtp->prettyDTypeNameQ() << " from "
2537 : : << fromDtp->prettyDTypeNameQ() << '\n'
2538 : : << nodep->warnMore()
2539 : : << "... Suggest try static cast");
2540 : : }
2541 : : nodep->replaceWithKeepDType(newp);
2542 : : VL_DO_DANGLING(pushDeletep(nodep), nodep);
2543 : : userIterate(newp, m_vup);
2544 : : }
2545 : : void visit(AstCastParse* nodep) override {
2546 : : // nodep->dtp could be data type, or a primary_constant
2547 : : // Don't iterate lhsp, will deal with that once convert the type
2548 : : V3Const::constifyParamsEdit(nodep->dtp()); // itemp may change
2549 : : if (AstConst* const constp = VN_CAST(nodep->dtp(), Const)) {
2550 : : constp->unlinkFrBack();
2551 : : AstNode* const newp
2552 : : = new AstCastSize{nodep->fileline(), nodep->lhsp()->unlinkFrBack(), constp};
2553 : : nodep->replaceWith(newp);
2554 : : VL_DO_DANGLING(pushDeletep(nodep), nodep);
2555 : : userIterate(newp, m_vup);
2556 : : } else if (AstNodeDType* const refp = VN_CAST(nodep->dtp(), NodeDType)) {
2557 : : refp->unlinkFrBack();
2558 : : AstNode* const newp = new AstCast{nodep->fileline(), nodep->lhsp()->unlinkFrBack(),
2559 : : VFlagChildDType{}, refp};
2560 : : nodep->replaceWith(newp);
2561 : : VL_DO_DANGLING(pushDeletep(nodep), nodep);
2562 : : userIterate(newp, m_vup);
2563 : : } else {
2564 : : nodep->v3warn(E_UNSUPPORTED,
2565 : : "Unsupported: Cast to " << nodep->dtp()->prettyTypeName());
2566 : : nodep->replaceWith(nodep->lhsp()->unlinkFrBack());
2567 : : VL_DO_DANGLING(pushDeletep(nodep), nodep);
2568 : : }
2569 : : }
2570 : : void visit(AstCast* nodep) override {
2571 : : if (nodep->didWidth()) return;
2572 : : UINFO(9, "CAST " << nodep);
2573 : : nodep->dtypep(iterateEditMoveDTypep(nodep, nodep->subDTypep()));
2574 : : if (m_vup->prelim()) {
2575 : : UINFOTREE(9, nodep, "", "CastPre");
2576 : : // UINFOTREE(1, nodep->backp(), "", "CastPreUpUp");
2577 : : if (AstSigned* const fromp = VN_CAST(nodep->fromp(), Signed)) {
2578 : : if (VN_IS(fromp->lhsp(), NodeStream)) {
2579 : : AstNode* const lhsp = fromp->lhsp()->unlinkFrBack();
2580 : : fromp->replaceWith(lhsp);
2581 : : VL_DO_DANGLING(fromp->deleteTree(), fromp);
2582 : : }
2583 : : }
2584 : : userIterateAndNext(nodep->fromp(), WidthVP{SELF, PRELIM}.p());
2585 : : UINFOTREE(9, nodep, "", "CastDit");
2586 : : AstNodeDType* const toDtp = nodep->dtypep()->skipRefToEnump();
2587 : : AstNodeDType* const fromDtp = nodep->fromp()->dtypep()->skipRefToEnump();
2588 : : const auto castable = AstNode::computeCastable(toDtp, fromDtp, nodep->fromp());
2589 : : bool bad = false;
2590 : : if (castable == VCastable::UNSUPPORTED) {
2591 : : nodep->v3warn(E_UNSUPPORTED, "Unsupported: static cast to "
2592 : : << toDtp->prettyDTypeNameQ() << " from "
2593 : : << fromDtp->prettyDTypeNameQ());
2594 : : bad = true;
2595 : : } else if (castable == VCastable::SAMEISH || castable == VCastable::COMPATIBLE
2596 : : || castable == VCastable::ENUM_IMPLICIT
2597 : : || castable == VCastable::ENUM_EXPLICIT) {
2598 : : ; // Continue
2599 : : } else if (castable == VCastable::DYNAMIC_CLASS) {
2600 : : nodep->v3error("Dynamic, not static cast, required to cast "
2601 : : << toDtp->prettyDTypeNameQ() << " from "
2602 : : << fromDtp->prettyDTypeNameQ() << '\n'
2603 : : << nodep->warnMore() << "... Suggest dynamic $cast");
2604 : : bad = true;
2605 : : } else if (castable == VCastable::INCOMPATIBLE) {
2606 : : nodep->v3error("Incompatible types to static cast to "
2607 : : << toDtp->prettyDTypeNameQ() << " from "
2608 : : << fromDtp->prettyDTypeNameQ() << '\n');
2609 : : bad = true;
2610 : : } else {
2611 : : nodep->v3fatalSrc("bad casting case");
2612 : : }
2613 : : // For now, replace it ASAP, so widthing can propagate easily
2614 : : // The cast may change signing, but we don't know the sign yet. Make it so.
2615 : : // Note we don't sign fromp() that would make the algorithm O(n^2) if lots of casting.
2616 : : AstNodeExpr* newp = nullptr;
2617 : : if (bad) {
2618 : : } else if (AstBasicDType* const basicp = toDtp->basicp()) {
2619 : : if (!basicp->isString() && fromDtp->isString()) {
2620 : : newp = new AstNToI{nodep->fileline(), nodep->fromp()->unlinkFrBack(), toDtp};
2621 : : } else if (!basicp->isDouble() && !fromDtp->isDouble()) {
2622 : : AstNodeDType* const origDTypep = nodep->dtypep();
2623 : : if (!VN_IS(fromDtp, StreamDType)) {
2624 : : const int width = toDtp->width();
2625 : : castSized(nodep, nodep->fromp(), width);
2626 : : }
2627 : : nodep->dtypeFrom(origDTypep); // If was enum, need dtype to preserve as enum
2628 : : // Note castSized might modify nodep->fromp()
2629 : : } else {
2630 : : iterateCheck(nodep, "value", nodep->fromp(), SELF, FINAL, fromDtp, EXTEND_EXP,
2631 : : false);
2632 : : }
2633 : : if (newp) {
2634 : : } else if (basicp->isDouble() && !nodep->fromp()->isDouble()) {
2635 : : if (nodep->fromp()->isSigned()) {
2636 : : newp = new AstISToRD{nodep->fileline(), nodep->fromp()->unlinkFrBack()};
2637 : : } else {
2638 : : newp = new AstIToRD{nodep->fileline(), nodep->fromp()->unlinkFrBack()};
2639 : : }
2640 : : } else if (!basicp->isDouble() && nodep->fromp()->isDouble()) {
2641 : : newp = new AstRToIRoundS{nodep->fileline(), nodep->fromp()->unlinkFrBack()};
2642 : : newp->dtypep(basicp);
2643 : : } else if (basicp->isSigned() && !nodep->fromp()->isSigned()) {
2644 : : newp = new AstSigned{nodep->fileline(), nodep->fromp()->unlinkFrBack()};
2645 : : } else if (!basicp->isSigned() && nodep->fromp()->isSigned()) {
2646 : : newp = new AstUnsigned{nodep->fileline(), nodep->fromp()->unlinkFrBack()};
2647 : : } else {
2648 : : // Can just remove cast, but need extend placeholder
2649 : : // so we can avoid warning message
2650 : : }
2651 : : } else if (VN_IS(toDtp, QueueDType)) {
2652 : : if (VN_IS(fromDtp, BasicDType)) {
2653 : : newp = new AstCvtPackedToArray{nodep->fileline(),
2654 : : nodep->fromp()->unlinkFrBack(), toDtp};
2655 : : } else if (VN_IS(fromDtp, QueueDType) || VN_IS(fromDtp, StreamDType)) {
2656 : : int srcElementBits = 1;
2657 : : int dstElementBits = 1;
2658 : : if (AstNodeDType* const elemDtp = fromDtp->subDTypep()) {
2659 : : srcElementBits = elemDtp->width();
2660 : : }
2661 : : const AstQueueDType* const dstQueueDtp = VN_AS(toDtp, QueueDType);
2662 : : if (AstNodeDType* const elemDtp = dstQueueDtp->subDTypep()) {
2663 : : dstElementBits = elemDtp->width();
2664 : : }
2665 : : newp = new AstCvtArrayToArray{nodep->fileline(),
2666 : : nodep->fromp()->unlinkFrBack(),
2667 : : toDtp,
2668 : : false,
2669 : : 1,
2670 : : dstElementBits,
2671 : : srcElementBits};
2672 : : }
2673 : : } else if (VN_IS(toDtp, ClassRefDType)) {
2674 : : // Can just remove cast
2675 : : } else {
2676 : : nodep->v3fatalSrc("Unimplemented: Casting non-simple data type "
2677 : : << toDtp->prettyDTypeNameQ());
2678 : : }
2679 : : if (!newp) newp = nodep->fromp()->unlinkFrBack();
2680 : : nodep->fromp(newp);
2681 : : UINFOTREE(9, nodep, "", "CastOut");
2682 : : // UINFOTREE(1, nodep->backp(), "", "CastOutUpUp");
2683 : : }
2684 : : if (m_vup->final()) {
2685 : : UINFOTREE(9, nodep, "", "CastFPit");
2686 : : iterateCheck(nodep, "value", nodep->fromp(), SELF, FINAL, nodep->fromp()->dtypep(),
2687 : : EXTEND_EXP, false);
2688 : : UINFOTREE(9, nodep, "", "CastFin");
2689 : : AstNodeExpr* const underp = nodep->fromp()->unlinkFrBack();
2690 : : underp->dtypeFrom(nodep);
2691 : : underp->didWidth(true);
2692 : : AstNodeExpr* const newp = new AstCastWrap{nodep->fileline(), underp};
2693 : : newp->didWidth(true);
2694 : : UINFOTREE(9, newp, "", "CastRep");
2695 : : nodep->replaceWith(newp);
2696 : : VL_DO_DANGLING(pushDeletep(nodep), nodep);
2697 : : }
2698 : : }
2699 : : void visit(AstCastSize* nodep) override {
2700 : : // IEEE: Signedness of result is same as self-determined signedness
2701 : : // However, the result is same as BITSEL, so we do not sign extend the LHS
2702 : : // UINFOTREE(1, nodep, "", "CastSizePre");
2703 : : assertAtExpr(nodep);
2704 : : if (m_vup->prelim()) {
2705 : : int width = nodep->rhsp()->toSInt();
2706 : : if (width < 1) {
2707 : : nodep->v3error("Size-changing cast to zero or negative size: " << width);
2708 : : width = 1;
2709 : : }
2710 : : userIterateAndNext(nodep->lhsp(), WidthVP{SELF, PRELIM}.p());
2711 : : castSized(nodep, nodep->lhsp(), width); // lhsp may change
2712 : : }
2713 : : if (m_vup->final()) {
2714 : : // CastSize not needed once sizes determined
2715 : : AstNode* const underp = nodep->lhsp()->unlinkFrBack();
2716 : : nodep->replaceWithKeepDType(underp);
2717 : : VL_DO_DANGLING(pushDeletep(nodep), nodep);
2718 : : }
2719 : : // UINFOTREE(1, nodep, "", "CastSizeOut");
2720 : : }
2721 : : void visit(AstCastWrap* nodep) override {
2722 : : // Inserted by V3Width only so we know has been resolved
2723 : : userIterateAndNext(nodep->lhsp(), WidthVP{nodep->dtypep(), BOTH}.p());
2724 : : }
2725 : : void castSized(AstNode* nodep, AstNode* underp, int width) {
2726 : : const AstBasicDType* underDtp = VN_CAST(underp->dtypep(), BasicDType);
2727 : : if (!underDtp) underDtp = underp->dtypep()->basicp();
2728 : : if (!underDtp) {
2729 : : nodep->v3warn(E_UNSUPPORTED, "Unsupported: Size-changing cast on non-basic data type");
2730 : : underDtp = VN_AS(nodep->findBitDType(), BasicDType);
2731 : : }
2732 : : UASSERT_OBJ(underp == nodep->op1p(), nodep, "Assuming op1 is cast value");
2733 : : // A cast propagates its size to the lower expression and is included in the maximum
2734 : : // width, so 23'(1'b1 + 1'b1) uses 23-bit math, but 1'(2'h2 * 2'h1) uses two-bit math.
2735 : : // However the output width is exactly that requested.
2736 : : // So two steps, first do the calculation's width (max of the two widths)
2737 : : {
2738 : : const int calcWidth = std::max(width, underDtp->width());
2739 : : AstNodeDType* const calcDtp = nodep->findBitOrLogicDType(
2740 : : calcWidth, calcWidth, underDtp->numeric(), underDtp->isFourstate());
2741 : : nodep->dtypep(calcDtp);
2742 : : // We ignore warnings as that is sort of the point of a cast
2743 : : iterateCheck(nodep, "Cast expr", underp, CONTEXT_DET, FINAL, calcDtp, EXTEND_EXP,
2744 : : false);
2745 : : VL_DANGLING(underp);
2746 : : underp = nodep->op1p(); // Above asserts that op1 was underp pre-relink
2747 : : }
2748 : : // UINFOTREE(1, nodep, "", "CastSizeClc");
2749 : : // Next step, make the proper output width
2750 : : {
2751 : : AstNodeDType* const outDtp = nodep->findBitOrLogicDType(
2752 : : width, width, underDtp->numeric(), underDtp->isFourstate());
2753 : : nodep->dtypep(outDtp);
2754 : : // We ignore warnings as that is sort of the point of a cast
2755 : : widthCheckSized(nodep, "Cast expr", VN_AS(underp, NodeExpr), outDtp, EXTEND_EXP,
2756 : : false);
2757 : : VL_DANGLING(underp);
2758 : : }
2759 : : }
2760 : : void visit(AstConstraintBefore* nodep) override {
2761 : : userIterateAndNext(nodep->lhssp(), WidthVP{SELF, BOTH}.p());
2762 : : userIterateAndNext(nodep->rhssp(), WidthVP{SELF, BOTH}.p());
2763 : : }
2764 : : void visit(AstConstraintExpr* nodep) override {
2765 : : userIterateAndNext(nodep->exprp(), WidthVP{SELF, BOTH}.p());
2766 : : }
2767 : : void visit(AstConstraintUnique* nodep) override {
2768 : : userIterateAndNext(nodep->rangesp(), WidthVP{SELF, BOTH}.p());
2769 : : }
2770 : : void visit(AstDistItem* nodep) override {
2771 : : userIterate(nodep->rangep(), m_vup);
2772 : : assertAtExpr(nodep);
2773 : : if (m_vup->prelim()) { // First stage evaluation
2774 : : userIterate(nodep->weightp(), WidthVP{SELF, BOTH}.p());
2775 : : }
2776 : : nodep->dtypep(nodep->rangep()->dtypep());
2777 : : }
2778 : : void visit(AstVar* nodep) override {
2779 : : // UINFOTREE(1, nodep, "", "InitPre");
2780 : : // Must have deterministic constant width
2781 : : // We can't skip this step when width()!=0, as creating a AstVar
2782 : : // with non-constant range gets size 1, not size 0. So use didWidth().
2783 : : if (nodep->didWidth()) return;
2784 : : if (nodep->doingWidth()) { // Early exit if have circular parameter definition
2785 : : UASSERT_OBJ(nodep->valuep(), nodep, "circular, but without value");
2786 : : nodep->v3error("Variable's initial value is circular: " << nodep->prettyNameQ());
2787 : : pushDeletep(nodep->valuep()->unlinkFrBack());
2788 : : nodep->valuep(new AstConst{nodep->fileline(), AstConst::BitTrue{}});
2789 : : nodep->dtypeFrom(nodep->valuep());
2790 : : nodep->didWidth(true);
2791 : : return;
2792 : : }
2793 : : nodep->doingWidth(true);
2794 : : // Make sure dtype is sized
2795 : : nodep->dtypep(iterateEditMoveDTypep(nodep, nodep->subDTypep()));
2796 : : UASSERT_OBJ(nodep->dtypep(), nodep, "No dtype determined for var");
2797 : : if (nodep->attrsp()) {
2798 : : nodep->attrsp()->foreach([this, nodep](AstAttrOf* attrp) {
2799 : : if (attrp->attrType() == VAttrType::VAR_PORT_DTYPE) {
2800 : : V3Const::constifyParamsEdit(attrp->fromp()); // fromp may change
2801 : : if (!similarDTypeRecurse(nodep->dtypep(), VN_AS(attrp->fromp(), NodeDType))) {
2802 : : nodep->dtypep()->v3error("Non-ANSI I/O declaration of signal "
2803 : : "conflicts with type declaration: "
2804 : : << nodep->prettyNameQ() << '\n'
2805 : : << nodep->dtypep()->warnContextPrimary() << '\n'
2806 : : << attrp->warnOther()
2807 : : << "... Location of other declaration\n"
2808 : : << attrp->warnContextSecondary());
2809 : : }
2810 : : VL_DO_DANGLING(pushDeletep(attrp->unlinkFrBack()), attrp);
2811 : : }
2812 : : });
2813 : : }
2814 : : if (m_ftaskp && m_ftaskp->dpiImport()) {
2815 : : AstNodeDType* dtp = nodep->dtypep();
2816 : : AstNodeDType* np = nullptr;
2817 : : while (VN_IS(dtp->skipRefp(), DynArrayDType)
2818 : : || VN_IS(dtp->skipRefp(), UnpackArrayDType)) {
2819 : : if (const AstDynArrayDType* const unsizedp
2820 : : = VN_CAST(dtp->skipRefp(), DynArrayDType)) {
2821 : : if (!np) {
2822 : : UINFO(9, "Dynamic becomes unsized array (var itself) " << nodep);
2823 : : } else {
2824 : : UINFO(9, "Dynamic becomes unsized array (subDType) " << dtp);
2825 : : }
2826 : : AstUnsizedArrayDType* const newp
2827 : : = new AstUnsizedArrayDType{unsizedp->fileline(), unsizedp->subDTypep()};
2828 : : newp->dtypep(newp);
2829 : : if (!np) { // for Var itself
2830 : : nodep->dtypep(newp);
2831 : : } else { // for subDType
2832 : : np->virtRefDTypep(newp);
2833 : : }
2834 : : v3Global.rootp()->typeTablep()->addTypesp(newp);
2835 : : np = newp;
2836 : : } else {
2837 : : np = dtp->skipRefp();
2838 : : }
2839 : : dtp = np->virtRefDTypep();
2840 : : }
2841 : : }
2842 : : if (AstWildcardArrayDType* const wildp
2843 : : = VN_CAST(nodep->dtypeSkipRefp(), WildcardArrayDType)) {
2844 : : nodep->dtypep(wildp); // Skip RefDType like for other dynamic array types
2845 : : }
2846 : : if (VN_IS(nodep->dtypep()->skipRefToConstp(), ConstDType)) nodep->isConst(true);
2847 : : // Parameters if implicit untyped inherit from what they are assigned to
2848 : : const AstBasicDType* const bdtypep = VN_CAST(nodep->dtypep(), BasicDType);
2849 : : bool didchk = false;
2850 : : const bool implicitParam = nodep->isParam() && bdtypep && bdtypep->implicit();
2851 : : if (implicitParam) {
2852 : : if (nodep->valuep()) {
2853 : : // Remove blanket deferral. We must attempt to visit the value to determine
2854 : : // type/deps. If it remains unresolved, specific node visitors (like AttrOf) should
2855 : : // handle deferral by setting a placeholder type to prevent "No dtype" errors
2856 : : // later.
2857 : : userIterateAndNext(nodep->valuep(), WidthVP{nodep->dtypep(), PRELIM}.p());
2858 : : UINFO(9, "implicitParamPRELIMIV " << nodep->valuep());
2859 : : // Although nodep will get a different width for parameters
2860 : : // just below, we want the init numbers to retain their
2861 : : // width/minwidth until parameters are replaced.
2862 : : // This prevents width warnings at the location the parameter is substituted in
2863 : : if (nodep->valuep()->isDouble()) {
2864 : : nodep->dtypeSetDouble();
2865 : : VL_DANGLING(bdtypep);
2866 : : } else if (nodep->valuep()->isString()) {
2867 : : nodep->dtypeSetString();
2868 : : VL_DANGLING(bdtypep);
2869 : : } else {
2870 : : int width = 0;
2871 : : AstNodeDType* const valueDTypep = nodep->valuep()->dtypep();
2872 : : UASSERT_OBJ(valueDTypep, nodep->valuep(),
2873 : : "Null dtype on implicit param value");
2874 : : const AstBasicDType* const valueBdtypep = valueDTypep->basicp();
2875 : : bool issigned = false;
2876 : : if (bdtypep->isNosign()) {
2877 : : if (valueBdtypep && valueBdtypep->isSigned()) issigned = true;
2878 : : } else {
2879 : : issigned = bdtypep->isSigned();
2880 : : }
2881 : : if (valueBdtypep->isString()) {
2882 : : // parameter X = "str", per IEEE is a number, not a string
2883 : : if (const auto* const constp = VN_CAST(nodep->valuep(), Const)) {
2884 : : if (constp->num().isString()) {
2885 : : width = constp->num().toString().length() * 8;
2886 : : }
2887 : : }
2888 : : if (width < 8) width = 8;
2889 : : } else if (nodep->valuep()->dtypep()->widthSized()) {
2890 : : width = nodep->valuep()->width();
2891 : : } else {
2892 : : if (nodep->valuep()->width() > 32) {
2893 : : nodep->valuep()->v3warn(
2894 : : WIDTH,
2895 : : "Assigning >32 bit to unranged parameter (defaults to 32 bits)");
2896 : : }
2897 : : width = 32;
2898 : : }
2899 : : // Can't just inherit valuep()->dtypep() as mwidth might not equal width
2900 : : if (width == 1) {
2901 : : // one bit parameter is same as "parameter [0] foo",
2902 : : // not "parameter logic foo" as you can extract
2903 : : // "foo[0]" from a parameter but not a wire
2904 : : nodep->dtypeChgWidthSigned(width, nodep->valuep()->widthMin(),
2905 : : VSigning::fromBool(issigned));
2906 : : nodep->dtypep(nodep->findLogicRangeDType(VNumRange{0, 0},
2907 : : nodep->valuep()->widthMin(),
2908 : : VSigning::fromBool(issigned)));
2909 : : } else {
2910 : : nodep->dtypeChgWidthSigned(width, nodep->valuep()->widthMin(),
2911 : : VSigning::fromBool(issigned));
2912 : : }
2913 : : didchk = true;
2914 : : }
2915 : : iterateCheckAssign(nodep, "Initial value", nodep->valuep(), FINAL,
2916 : : nodep->dtypep());
2917 : : UINFO(9, "implicitParamFromIV " << nodep->valuep());
2918 : : // UINFO below will print variable nodep
2919 : : } else {
2920 : : // Or, if nothing assigned, they're integral
2921 : : nodep->dtypeSetSigned32();
2922 : : VL_DANGLING(bdtypep);
2923 : : }
2924 : : } else if (bdtypep && bdtypep->implicit()) { // Implicits get converted to size 1
2925 : : nodep->dtypeSetLogicSized(1, bdtypep->numeric());
2926 : : VL_DANGLING(bdtypep);
2927 : : }
2928 : : if (nodep->isNet()) {
2929 : : AstNodeDType* const badDtp = dtypeNot4StateIntegralRecurse(nodep->dtypep());
2930 : : if (badDtp)
2931 : : nodep->v3error(
2932 : : "Net " << nodep->prettyNameQ()
2933 : : << " data type must be 4-state integral or array/union/struct of such"
2934 : : << " (IEEE 1800-2023 6.7.1)\n"
2935 : : << nodep->warnContextPrimary() << '\n'
2936 : : << badDtp->warnOther() << "... Location of failing data type "
2937 : : << badDtp->prettyDTypeNameQ() << '\n'
2938 : : << badDtp->warnContextSecondary());
2939 : : }
2940 : : if (nodep->valuep() && !didchk) {
2941 : : // UINFOTREE(1, nodep, "", "final");
2942 : : // AstPattern requires assignments to pass datatype on PRELIM
2943 : : userIterateAndNext(nodep->valuep(), WidthVP{nodep->dtypep(), PRELIM}.p());
2944 : : iterateCheckAssign(nodep, "Initial value", nodep->valuep(), FINAL, nodep->dtypep());
2945 : : }
2946 : : userIterateAndNext(nodep->delayp(), WidthVP{nodep->dtypep(), PRELIM}.p());
2947 : : UINFO(4, "varWidthed " << nodep);
2948 : : // UINFOTREE(1, nodep, "", "InitOut");
2949 : : nodep->didWidth(true);
2950 : : nodep->doingWidth(false);
2951 : : }
2952 : : void visit(AstNodeVarRef* nodep) override {
2953 : : if (nodep->didWidth()) return;
2954 : : if (!nodep->varp()) {
2955 : : if (m_paramsOnly && VN_IS(nodep, VarXRef)) {
2956 : : checkConstantOrReplace(
2957 : : nodep, false,
2958 : : "Parameter-resolved constants must not use dotted references: "
2959 : : + nodep->prettyNameQ());
2960 : : VL_DANGLING(nodep);
2961 : : return;
2962 : : } else {
2963 : : nodep->v3fatalSrc("Unlinked varref");
2964 : : }
2965 : : }
2966 : : if (!nodep->varp()->didWidth()) {
2967 : : // Var hasn't been widthed, so make it so.
2968 : : userIterate(nodep->varp(), nullptr);
2969 : : }
2970 : : // UINFOTREE(9, nodep, "", "VRin");
2971 : : // UINFOTREE(9, nodep->varp(), "", "forvar");
2972 : : // Note genvar's are also entered as integers
2973 : : nodep->dtypeFrom(nodep->varp());
2974 : : if (VN_IS(nodep->backp(), NodeAssign) && nodep->access().isWriteOrRW()) { // On LHS
2975 : : UASSERT_OBJ(nodep->dtypep(), nodep, "LHS var should be dtype completed");
2976 : : }
2977 : : // UINFOTREE(9, nodep, "", "VRout");
2978 : : if (nodep->access().isWriteOrRW() && nodep->varp()->direction() == VDirection::CONSTREF) {
2979 : : nodep->v3error("Assigning to const ref variable: " << nodep->prettyNameQ());
2980 : : } else if (nodep->access().isWriteOrRW() && nodep->varp()->isInput()
2981 : : && !nodep->varp()->isFuncLocal() && nodep->varp()->isReadOnly()
2982 : : && (!m_ftaskp || !m_ftaskp->isConstructor())
2983 : : && !VN_IS(m_procedurep, InitialAutomatic) && !VN_IS(m_procedurep, InitialStatic)
2984 : : && !VN_IS(nodep->abovep(), AssignForce) && !VN_IS(nodep->abovep(), Release)) {
2985 : : // Skip ASSIGNIN for continuous assignments to net-type input ports
2986 : : // via hierarchical reference. Net ports allow multiple continuous
2987 : : // drivers (IEEE 1800-2023 23.3.3.3). Input ports default to net
2988 : : // when port kind is omitted (23.2.2.3, PORT type).
2989 : : const bool hierRef = !m_curModVars.count(nodep->varp());
2990 : : const bool netPort
2991 : : = nodep->varp()->isNet() || nodep->varp()->varType() == VVarType::PORT;
2992 : : const bool contAssign = VN_IS(nodep->abovep(), AssignW);
2993 : : const bool pinConn = VN_IS(nodep->abovep(), Pin);
2994 : : if (!(hierRef && netPort && (contAssign || pinConn))) {
2995 : : nodep->v3warn(ASSIGNIN,
2996 : : "Assigning to input/const variable: " << nodep->prettyNameQ());
2997 : : }
2998 : : } else if (nodep->access().isWriteOrRW() && nodep->varp()->isConst() && !m_paramsOnly
2999 : : && (!m_ftaskp || !m_ftaskp->isConstructor())
3000 : : && !VN_IS(m_procedurep, InitialAutomatic) && !VN_IS(m_procedurep, InitialStatic)
3001 : : && !m_underMemberSel) {
3002 : : // Too loose, but need to allow our generated first assignment
3003 : : // Move this to a property of the AstInitial block
3004 : : nodep->v3warn(E_CONSTWRITTEN, "Writing to 'const' data-typed variable "
3005 : : << nodep->prettyNameQ()
3006 : : << " (IEEE 1800-2023 6.20.6)");
3007 : : }
3008 : : if (nodep->varp()->isClassMember() && !nodep->varp()->isFuncLocal()
3009 : : && !nodep->varp()->lifetime().isStatic() && m_ftaskp && m_ftaskp->isStatic()) {
3010 : : nodep->v3error("Cannot access non-static member variable "
3011 : : << nodep->prettyNameQ() << " from a static method "
3012 : : << m_ftaskp->prettyNameQ() << " without object (IEEE 1800-2023 8.10)");
3013 : : }
3014 : : nodep->didWidth(true);
3015 : : }
3016 : :
3017 : : void visit(AstEnumDType* nodep) override {
3018 : : if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed
3019 : : UINFO(5, " ENUMDTYPE " << nodep);
3020 : : nodep->refDTypep(iterateEditMoveDTypep(nodep, nodep->subDTypep()));
3021 : : nodep->dtypep(nodep);
3022 : : AstNodeDType* basicp = nodep->dtypep()->skipRefp()->basicp();
3023 : : AstNodeDType* const badDtp = dtypeNotIntAtomOrVecRecurse(nodep->subDTypep());
3024 : : if (badDtp) {
3025 : : nodep->v3error(
3026 : : "Enum data type must be an integer atom or vector type (IEEE 1800-2023 6.19)\n"
3027 : : << nodep->warnContextPrimary() << '\n'
3028 : : << badDtp->warnOther() << "... Location of failing data type "
3029 : : << badDtp->prettyDTypeNameQ() << '\n'
3030 : : << badDtp->warnContextSecondary());
3031 : : basicp = nodep->findSigned32DType()->basicp();
3032 : : nodep->refDTypep(basicp);
3033 : : }
3034 : : nodep->widthFromSub(nodep->subDTypep());
3035 : : // Assign widths
3036 : : userIterateAndNext(nodep->itemsp(), WidthVP{nodep->dtypep(), BOTH}.p());
3037 : : // Assign missing values
3038 : : V3Number num(nodep, nodep->width(), 0);
3039 : : const V3Number one{nodep, nodep->width(), 1};
3040 : : bool wrapAround = false;
3041 : : std::map<const V3Number, AstEnumItem*> inits;
3042 : : for (AstEnumItem* itemp = nodep->itemsp(); itemp;
3043 : : itemp = VN_AS(itemp->nextp(), EnumItem)) {
3044 : : if (itemp->valuep()) {
3045 : : if (debug() >= 9) {
3046 : : UINFO(0, "EnumInit " << itemp);
3047 : : itemp->valuep()->dumpTree("- EnumInit: ");
3048 : : }
3049 : : V3Const::constifyParamsEdit(itemp->valuep()); // itemp may change
3050 : : if (!VN_IS(itemp->valuep(), Const)) {
3051 : : itemp->valuep()->v3error("Enum value isn't a constant");
3052 : : itemp->valuep()->unlinkFrBack()->deleteTree();
3053 : : continue;
3054 : : }
3055 : : // TODO IEEE says assigning sized number that is not same size as enum is illegal
3056 : : wrapAround = false; // Explicit value resets wrap-around tracking
3057 : : }
3058 : : if (!itemp->valuep()) {
3059 : : if (wrapAround) {
3060 : : itemp->v3error("Enum value illegally wrapped around (IEEE 1800-2023 6.19)");
3061 : : }
3062 : : if (num.isFourState()) {
3063 : : itemp->v3error("Enum value that is unassigned cannot follow value with X/Zs "
3064 : : "(IEEE 1800-2023 6.19)");
3065 : : }
3066 : : itemp->valuep(new AstConst{itemp->fileline(), num});
3067 : : }
3068 : :
3069 : : const AstConst* const constp = VN_AS(itemp->valuep(), Const);
3070 : : if (constp->num().isFourState() && basicp->basicp() && !basicp->isFourstate()) {
3071 : : itemp->v3error("Enum value with X/Zs cannot be assigned to non-fourstate type "
3072 : : "(IEEE 1800-2023 6.19)");
3073 : : }
3074 : : num.opAssign(constp->num());
3075 : : // Look for duplicates
3076 : : const auto pair = inits.emplace(num, itemp);
3077 : : if (!pair.second) { // IEEE says illegal
3078 : : const AstNode* const otherp = pair.first->second;
3079 : : itemp->v3error("Overlapping enumeration value: "
3080 : : << itemp->prettyNameQ() << '\n'
3081 : : << itemp->warnContextPrimary() << '\n'
3082 : : << otherp->warnOther() << "... Location of original declaration\n"
3083 : : << otherp->warnContextSecondary());
3084 : : }
3085 : : // Detect wrap-around after increment for the next auto-valued item.
3086 : : // For signed types, overflow is positive-to-negative (e.g., INT_MAX+1).
3087 : : // For unsigned types, overflow wraps to zero (e.g., UINT_MAX+1).
3088 : : const bool prevNeg = constp->num().isNegative();
3089 : : num.opAdd(one, constp->num());
3090 : : if (basicp->isSigned()) {
3091 : : wrapAround = !prevNeg && num.isNegative();
3092 : : } else {
3093 : : wrapAround = num.isEqZero();
3094 : : }
3095 : : }
3096 : : }
3097 : : void visit(AstEnumItem* nodep) override {
3098 : : UINFO(5, " ENUMITEM " << nodep);
3099 : : VL_RESTORER(m_enumItemp);
3100 : : m_enumItemp = nodep;
3101 : : AstNodeDType* const vdtypep = m_vup->dtypep();
3102 : : UASSERT_OBJ(vdtypep, nodep, "ENUMITEM not under ENUM");
3103 : : nodep->dtypep(vdtypep);
3104 : : if (nodep->valuep()) { // else the value will be assigned sequentially
3105 : : // Default type is int, but common to assign narrower values, so minwidth from value
3106 : : userIterateAndNext(nodep->valuep(), WidthVP{CONTEXT_DET, PRELIM}.p());
3107 : : bool warnOn = true;
3108 : : AstNodeExpr* valuep = nodep->valuep();
3109 : : if (const AstAdd* const anodep = VN_CAST(valuep, Add)) {
3110 : : // If constructed by V3LinkParse due to "enumitem[N_REPEATS] value"
3111 : : if (anodep->fileline()->equalFirstLineCol(*(nodep->fileline())))
3112 : : valuep = anodep->lhsp();
3113 : : }
3114 : : if (const AstConst* const constp = VN_CAST(valuep, Const)) {
3115 : : if (static_cast<int>(constp->num().mostSetBitP1()) > nodep->width()) {
3116 : : constp->v3warn(ENUMITEMWIDTH,
3117 : : "Enum value exceeds width of enum type (IEEE 1800-2023 6.19)");
3118 : : warnOn = false; // Prevent normal WIDTHTRUNC
3119 : : }
3120 : : }
3121 : : // Minwidth does not come from value, as spec says set based on parent
3122 : : // and if we keep minwidth we'll consider it unsized which is incorrect
3123 : : iterateCheck(nodep, "Enum value", nodep->valuep(), CONTEXT_DET, FINAL, nodep->dtypep(),
3124 : : EXTEND_EXP, warnOn);
3125 : : // Always create a cast, to avoid later ENUMVALUE warnings
3126 : : nodep->valuep(new AstCast{nodep->valuep()->fileline(), nodep->valuep()->unlinkFrBack(),
3127 : : nodep->dtypep()});
3128 : : }
3129 : : }
3130 : : void visit(AstEnumItemRef* nodep) override {
3131 : : UASSERT_OBJ(nodep->itemp(), nodep, "Unlinked");
3132 : : if (!nodep->itemp()->didWidth()) {
3133 : : // We need to do the whole enum en masse
3134 : : AstNode* enump = nodep->itemp();
3135 : : UASSERT_OBJ(enump, nodep, "EnumItemRef not linked");
3136 : : for (; enump; enump = enump->backp()) {
3137 : : if (VN_IS(enump, EnumDType)) break;
3138 : : }
3139 : : UASSERT_OBJ(enump, nodep, "EnumItemRef can't deref back to an Enum");
3140 : : VL_DO_DANGLING(userIterate(enump, m_vup), enump); // Parent enump maybe relinked
3141 : : }
3142 : : nodep->dtypeFrom(nodep->itemp());
3143 : : }
3144 : : void visit(AstConsAssoc* nodep) override {
3145 : : // Type computed when constructed here
3146 : : auto* const vdtypep = VN_AS(m_vup->dtypep()->skipRefp(), AssocArrayDType);
3147 : : UASSERT_OBJ(vdtypep, nodep, "ConsAssoc requires assoc upper parent data type");
3148 : : if (m_vup->prelim()) {
3149 : : nodep->dtypeFrom(vdtypep);
3150 : : if (nodep->defaultp()) {
3151 : : iterateCheck(nodep, "default", nodep->defaultp(), CONTEXT_DET, FINAL,
3152 : : vdtypep->subDTypep(), EXTEND_EXP);
3153 : : }
3154 : : }
3155 : : }
3156 : : void visit(AstSetAssoc* nodep) override {
3157 : : // Type computed when constructed here
3158 : : auto* const vdtypep = VN_AS(m_vup->dtypep()->skipRefp(), AssocArrayDType);
3159 : : UASSERT_OBJ(vdtypep, nodep, "SetsAssoc requires assoc upper parent data type");
3160 : : if (m_vup->prelim()) {
3161 : : nodep->dtypeFrom(vdtypep);
3162 : : userIterateAndNext(nodep->lhsp(), WidthVP{vdtypep, BOTH}.p());
3163 : : iterateCheck(nodep, "key", nodep->keyp(), CONTEXT_DET, FINAL, vdtypep->keyDTypep(),
3164 : : EXTEND_EXP);
3165 : : iterateCheck(nodep, "value", nodep->valuep(), CONTEXT_DET, FINAL, vdtypep->subDTypep(),
3166 : : EXTEND_EXP);
3167 : : }
3168 : : }
3169 : : void visit(AstConsWildcard* nodep) override {
3170 : : // Type computed when constructed here
3171 : : auto* const vdtypep = VN_AS(m_vup->dtypep()->skipRefp(), WildcardArrayDType);
3172 : : UASSERT_OBJ(vdtypep, nodep, "ConsWildcard requires wildcard upper parent data type");
3173 : : if (m_vup->prelim()) {
3174 : : nodep->dtypeFrom(vdtypep);
3175 : : if (nodep->defaultp()) {
3176 : : iterateCheck(nodep, "default", nodep->defaultp(), CONTEXT_DET, FINAL,
3177 : : vdtypep->subDTypep(), EXTEND_EXP);
3178 : : }
3179 : : }
3180 : : }
3181 : : void visit(AstSetWildcard* nodep) override {
3182 : : // Type computed when constructed here
3183 : : auto* const vdtypep = VN_AS(m_vup->dtypep()->skipRefp(), WildcardArrayDType);
3184 : : UASSERT_OBJ(vdtypep, nodep, "SetWildcard requires wildcard upper parent data type");
3185 : : if (m_vup->prelim()) {
3186 : : nodep->dtypeFrom(vdtypep);
3187 : : userIterateAndNext(nodep->lhsp(), WidthVP{vdtypep, BOTH}.p());
3188 : : iterateCheck(nodep, "key", nodep->keyp(), CONTEXT_DET, FINAL,
3189 : : vdtypep->findStringDType(), EXTEND_EXP);
3190 : : iterateCheck(nodep, "value", nodep->valuep(), CONTEXT_DET, FINAL, vdtypep->subDTypep(),
3191 : : EXTEND_EXP);
3192 : : }
3193 : : }
3194 : : void visit(AstConsPackUOrStruct* nodep) override {
3195 : : // Type was computed when constructed in V3Width earlier
3196 : : auto* const vdtypep = VN_AS(nodep->dtypep()->skipRefp(), NodeUOrStructDType);
3197 : : UASSERT_OBJ(vdtypep, nodep, "ConsPackUOrStruct requires packed array parent data type");
3198 : : userIterateChildren(nodep, WidthVP{vdtypep, BOTH}.p());
3199 : : }
3200 : : void visit(AstConsPackMember* nodep) override {
3201 : : // Type was computed when constructed in V3Width earlier
3202 : : auto* const vdtypep = VN_AS(nodep->dtypep(), MemberDType);
3203 : : UASSERT_OBJ(vdtypep, nodep, "ConsPackMember requires member data type");
3204 : : if (m_vup->prelim()) userIterateAndNext(nodep->rhsp(), WidthVP{vdtypep, BOTH}.p());
3205 : : }
3206 : : void visit(AstConsDynArray* nodep) override {
3207 : : // Type computed when constructed here
3208 : : AstDynArrayDType* const vdtypep = VN_AS(m_vup->dtypep()->skipRefp(), DynArrayDType);
3209 : : UASSERT_OBJ(vdtypep, nodep, "ConsDynArray requires queue upper parent data type");
3210 : : if (m_vup->prelim()) {
3211 : : AstNodeDType* const lhsDtp = nodep->lhsIsValue() ? vdtypep->subDTypep() : vdtypep;
3212 : : AstNodeDType* const rhsDtp = nodep->rhsIsValue() ? vdtypep->subDTypep() : vdtypep;
3213 : : userIterateAndNext(nodep->lhsp(), WidthVP{lhsDtp, PRELIM}.p());
3214 : : userIterateAndNext(nodep->rhsp(), WidthVP{rhsDtp, PRELIM}.p());
3215 : : nodep->dtypeFrom(vdtypep);
3216 : : }
3217 : : if (m_vup->final()) {
3218 : : if (nodep->didWidthAndSet()) return;
3219 : : // Arguments can be either elements of the queue or a queue itself
3220 : : // Concats (part of tree of concats) must always become ConsDynArray's
3221 : : AstNodeDType* const lhsDtp = nodep->lhsIsValue() ? vdtypep->subDTypep() : vdtypep;
3222 : : AstNodeDType* const rhsDtp = nodep->rhsIsValue() ? vdtypep->subDTypep() : vdtypep;
3223 : : if (nodep->lhsp()) {
3224 : : if (nodep->lhsIsValue()) {
3225 : : // Sub elements are not queues, but concats, must always pass concats down
3226 : : iterateCheckTyped(nodep, "LHS", nodep->lhsp(), lhsDtp, FINAL);
3227 : : } else {
3228 : : userIterateAndNext(nodep->lhsp(), WidthVP{lhsDtp, FINAL}.p());
3229 : : }
3230 : : }
3231 : : if (nodep->rhsp()) {
3232 : : if (nodep->rhsIsValue()) {
3233 : : iterateCheckTyped(nodep, "RHS", nodep->rhsp(), rhsDtp, FINAL);
3234 : : } else {
3235 : : userIterateAndNext(nodep->rhsp(), WidthVP{rhsDtp, FINAL}.p());
3236 : : }
3237 : : }
3238 : : if (nodep->didWidthAndSet()) return;
3239 : : nodep->dtypeFrom(vdtypep);
3240 : : }
3241 : : }
3242 : : void visit(AstConsQueue* nodep) override {
3243 : : // Type computed when constructed here
3244 : : AstQueueDType* const vdtypep = VN_AS(m_vup->dtypep()->skipRefp(), QueueDType);
3245 : : UASSERT_OBJ(vdtypep, nodep, "ConsQueue requires queue upper parent data type");
3246 : : if (m_vup->prelim()) {
3247 : : AstNodeDType* const lhsDtp = nodep->lhsIsValue() ? vdtypep->subDTypep() : vdtypep;
3248 : : AstNodeDType* const rhsDtp = nodep->rhsIsValue() ? vdtypep->subDTypep() : vdtypep;
3249 : : userIterateAndNext(nodep->lhsp(), WidthVP{lhsDtp, PRELIM}.p());
3250 : : userIterateAndNext(nodep->rhsp(), WidthVP{rhsDtp, PRELIM}.p());
3251 : : nodep->dtypeFrom(vdtypep);
3252 : : }
3253 : : if (m_vup->final()) {
3254 : : if (nodep->didWidthAndSet()) return;
3255 : : // Arguments can be either elements of the queue or a queue itself
3256 : : // Concats (part of tree of concats) must always become ConsQueue's
3257 : : AstNodeDType* const lhsDtp = nodep->lhsIsValue() ? vdtypep->subDTypep() : vdtypep;
3258 : : AstNodeDType* const rhsDtp = nodep->rhsIsValue() ? vdtypep->subDTypep() : vdtypep;
3259 : : if (nodep->lhsp()) {
3260 : : if (nodep->lhsIsValue()) {
3261 : : // Sub elements are not queues, but concats, must always pass concats down
3262 : : iterateCheckTyped(nodep, "LHS", nodep->lhsp(), lhsDtp, FINAL);
3263 : : } else {
3264 : : userIterateAndNext(nodep->lhsp(), WidthVP{lhsDtp, FINAL}.p());
3265 : : }
3266 : : }
3267 : : if (nodep->rhsp()) {
3268 : : if (nodep->rhsIsValue()) {
3269 : : iterateCheckTyped(nodep, "RHS", nodep->rhsp(), rhsDtp, FINAL);
3270 : : } else {
3271 : : userIterateAndNext(nodep->rhsp(), WidthVP{rhsDtp, FINAL}.p());
3272 : : }
3273 : : }
3274 : : nodep->dtypeFrom(vdtypep);
3275 : : }
3276 : : }
3277 : : void visit(AstInitItem* nodep) override { //
3278 : : userIterateChildren(nodep, m_vup);
3279 : : }
3280 : : void visit(AstInitArray* nodep) override {
3281 : : // InitArray has type of the array; children are array values
3282 : : assertAtExpr(nodep);
3283 : : if (m_vup->prelim()) { // First stage evaluation
3284 : : AstNodeDType* const vdtypep = m_vup->dtypeNullp();
3285 : : if (!nodep->dtypep() || vdtypep) {
3286 : : UASSERT_OBJ(vdtypep, nodep,
3287 : : "InitArray type not assigned by AstPattern/Var visitor");
3288 : : nodep->dtypep(vdtypep);
3289 : : }
3290 : : const AstNodeDType* const arrayp = nodep->dtypep()->skipRefp();
3291 : : if (VN_IS(arrayp, NodeArrayDType) || VN_IS(arrayp, AssocArrayDType)) {
3292 : : userIterateChildren(nodep, WidthVP{arrayp->subDTypep(), BOTH}.p());
3293 : : } else {
3294 : : UINFO(1, "on " << nodep);
3295 : : UINFO(1, "dtype object " << arrayp);
3296 : : nodep->v3fatalSrc("InitArray on non-array");
3297 : : }
3298 : : }
3299 : : }
3300 : : void visit(AstDist* nodep) override {
3301 : : // x dist {a :/ p, b :/ q} --> (p > 0 && x == a) || (q > 0 && x == b)
3302 : : // (only outside constraints; inside constraints V3Randomize handles weighted selection)
3303 : : userIterateAndNext(nodep->exprp(), WidthVP{CONTEXT_DET, PRELIM}.p());
3304 : : for (AstNode *nextip, *itemp = nodep->itemsp(); itemp; itemp = nextip) {
3305 : : nextip = itemp->nextp(); // iterate may cause the node to get replaced
3306 : : VL_DO_DANGLING(userIterate(itemp, WidthVP{CONTEXT_DET, PRELIM}.p()), itemp);
3307 : : }
3308 : :
3309 : : AstBasicDType* dtype = VN_CAST(nodep->exprp()->dtypep(), BasicDType);
3310 : : AstNodeDType* subDTypep = nullptr;
3311 : : nodep->dtypeSetBit();
3312 : :
3313 : : if (dtype && dtype->isString()) {
3314 : : nodep->dtypeSetString();
3315 : : subDTypep = nodep->findStringDType();
3316 : : } else if (dtype && dtype->isDouble()) {
3317 : : nodep->dtypeSetDouble();
3318 : : subDTypep = nodep->findDoubleDType();
3319 : : } else {
3320 : : // Take width as maximum across all items
3321 : : int width = nodep->exprp()->width();
3322 : : int mwidth = nodep->exprp()->widthMin();
3323 : : for (const AstNode* itemp = nodep->itemsp(); itemp; itemp = itemp->nextp()) {
3324 : : width = std::max(width, itemp->width());
3325 : : mwidth = std::max(mwidth, itemp->widthMin());
3326 : : }
3327 : : subDTypep = nodep->findLogicDType(width, mwidth, nodep->exprp()->dtypep()->numeric());
3328 : : }
3329 : :
3330 : : iterateCheck(nodep, "Dist expression", nodep->exprp(), CONTEXT_DET, FINAL, subDTypep,
3331 : : EXTEND_EXP);
3332 : : for (AstNode *nextip, *itemp = nodep->itemsp(); itemp; itemp = nextip) {
3333 : : nextip = itemp->nextp();
3334 : : itemp = VN_AS(itemp, DistItem)->rangep();
3335 : : // InsideRange will get replaced with Lte&Gte and finalized later
3336 : : if (!VN_IS(itemp, InsideRange))
3337 : : iterateCheck(nodep, "Dist Item", itemp, CONTEXT_DET, FINAL, subDTypep, EXTEND_EXP);
3338 : : }
3339 : :
3340 : : // Inside a constraint, V3Randomize handles dist lowering with proper weights,
3341 : : // but only for simple scalar/range items. Container-type items (queues, arrays)
3342 : : // must be lowered here via insideItem() which knows how to expand them.
3343 : : if (m_constraintp) {
3344 : : bool canLower = true;
3345 : : for (AstDistItem* ditemp = nodep->itemsp(); ditemp;
3346 : : ditemp = VN_AS(ditemp->nextp(), DistItem)) {
3347 : : if (!VN_IS(ditemp->rangep(), Const) && !VN_IS(ditemp->rangep(), InsideRange)) {
3348 : : canLower = false;
3349 : : break;
3350 : : }
3351 : : }
3352 : : if (canLower) return;
3353 : : }
3354 : :
3355 : : // Outside constraint: lower to inside (ignores weights)
3356 : : AstNodeExpr* newp = nullptr;
3357 : : for (AstDistItem* itemp = nodep->itemsp(); itemp;
3358 : : itemp = VN_AS(itemp->nextp(), DistItem)) {
3359 : : AstNodeExpr* inewp
3360 : : = insideItem(nodep, nodep->exprp()->cloneTreePure(false), itemp->rangep());
3361 : : if (!inewp) continue;
3362 : : AstNodeExpr* const cmpp
3363 : : = new AstGt{itemp->fileline(), itemp->weightp()->unlinkFrBack(),
3364 : : new AstConst{itemp->fileline(), 0}};
3365 : : cmpp->fileline()->modifyWarnOff(V3ErrorCode::UNSIGNED, true);
3366 : : cmpp->fileline()->modifyWarnOff(V3ErrorCode::CMPCONST, true);
3367 : : inewp = new AstLogAnd{itemp->fileline(), cmpp, inewp};
3368 : : newp = newp ? new AstLogOr{nodep->fileline(), newp, inewp} : inewp;
3369 : : }
3370 : : if (!newp) newp = new AstConst{nodep->fileline(), AstConst::BitFalse{}};
3371 : :
3372 : : UINFOTREE(9, nodep, "", "dist-out");
3373 : : nodep->replaceWith(newp);
3374 : : VL_DO_DANGLING(pushDeletep(nodep), nodep);
3375 : : }
3376 : :
3377 : : void visit(AstInside* nodep) override {
3378 : : userIterateAndNext(nodep->exprp(), WidthVP{CONTEXT_DET, PRELIM}.p());
3379 : : for (AstNode *nextip, *itemp = nodep->itemsp(); itemp; itemp = nextip) {
3380 : : nextip = itemp->nextp(); // iterate may cause the node to get replaced
3381 : : VL_DO_DANGLING(userIterate(itemp, WidthVP{CONTEXT_DET, PRELIM}.p()), itemp);
3382 : : }
3383 : :
3384 : : AstBasicDType* dtype = VN_CAST(nodep->exprp()->dtypep(), BasicDType);
3385 : : AstNodeDType* expDTypep = nullptr;
3386 : :
3387 : : if (dtype && dtype->isString()) {
3388 : : nodep->dtypeSetString();
3389 : : expDTypep = nodep->findStringDType();
3390 : : } else if (dtype && dtype->isDouble()) {
3391 : : nodep->dtypeSetDouble();
3392 : : expDTypep = nodep->findDoubleDType();
3393 : : } else {
3394 : : // Take width as maximum across all items
3395 : : int width = nodep->exprp()->width();
3396 : : int mwidth = nodep->exprp()->widthMin();
3397 : : bool isFourstate = nodep->exprp()->dtypep()->isFourstate();
3398 : : for (const AstNode* itemp = nodep->itemsp(); itemp; itemp = itemp->nextp()) {
3399 : : width = std::max(width, itemp->width());
3400 : : mwidth = std::max(mwidth, itemp->widthMin());
3401 : : isFourstate |= itemp->dtypep()->isFourstate();
3402 : : }
3403 : : nodep->dtypeSetBit();
3404 : : const VSigning numeric = nodep->exprp()->dtypep()->numeric();
3405 : : expDTypep = nodep->findBitOrLogicDType(width, mwidth, numeric, isFourstate);
3406 : : }
3407 : :
3408 : : iterateCheck(nodep, "Inside expression", nodep->exprp(), CONTEXT_DET, FINAL, expDTypep,
3409 : : EXTEND_EXP);
3410 : : for (AstNode *nextip, *itemp = nodep->itemsp(); itemp; itemp = nextip) {
3411 : : nextip = itemp->nextp(); // iterate may cause the node to get replaced
3412 : : // InsideRange will get replaced with Lte&Gte and finalized later
3413 : : if (!VN_IS(itemp, InsideRange) && !itemp->dtypep()->isNonPackedArray())
3414 : : iterateCheck(nodep, "Inside Item", itemp, CONTEXT_DET, FINAL, expDTypep,
3415 : : EXTEND_EXP);
3416 : : }
3417 : :
3418 : : AstNodeExpr* exprp;
3419 : : AstExprStmt* exprStmtp = nullptr;
3420 : : // Skip constraints since those are declarative expressions and they are never actually
3421 : : // executed so, there is no need for purification since they cannot generate sideeffects.
3422 : : if (!m_constraintp && !nodep->exprp()->isPure()) {
3423 : : FileLine* const fl = nodep->exprp()->fileline();
3424 : : // Ensure sized dtype for temp variable
3425 : : AstNodeDType* const exprDtp = nodep->exprp()->dtypep();
3426 : : const int w = exprDtp->width();
3427 : : AstNodeDType* const tempDTypep
3428 : : = exprDtp->widthSized() ? exprDtp
3429 : : : nodep->findBitOrLogicDType(w, w, exprDtp->numeric(),
3430 : : exprDtp->isFourstate());
3431 : : AstVar* const varp
3432 : : = new AstVar{fl, VVarType::XTEMP, m_insideTempNames.get(nodep), tempDTypep};
3433 : : exprp = new AstVarRef{fl, varp, VAccess::READ};
3434 : : exprStmtp = new AstExprStmt{fl,
3435 : : new AstAssign{fl, new AstVarRef{fl, varp, VAccess::WRITE},
3436 : : nodep->exprp()->unlinkFrBack()},
3437 : : exprp};
3438 : : if (m_ftaskp) {
3439 : : varp->funcLocal(true);
3440 : : varp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
3441 : : m_ftaskp->stmtsp()->addHereThisAsNext(varp);
3442 : : } else {
3443 : : m_modep->stmtsp()->addHereThisAsNext(varp);
3444 : : }
3445 : : iterate(varp);
3446 : : iterate(exprStmtp);
3447 : : } else {
3448 : : exprp = nodep->exprp();
3449 : : }
3450 : : UINFOTREE(9, nodep, "", "inside-in");
3451 : : // Now rip out the inside and replace with simple math
3452 : : AstNodeExpr* newp = nullptr;
3453 : : for (AstNodeExpr *nextip, *itemp = nodep->itemsp(); itemp; itemp = nextip) {
3454 : : nextip = VN_AS(itemp->nextp(), NodeExpr); // Will be unlinking
3455 : : AstNodeExpr* inewp;
3456 : : if (exprStmtp) {
3457 : : inewp = insideItem(nodep, exprStmtp, itemp);
3458 : : exprStmtp = nullptr;
3459 : : } else {
3460 : : inewp = insideItem(nodep, exprp->cloneTreePure(false), itemp);
3461 : : }
3462 : : if (!inewp) continue;
3463 : : newp = newp ? new AstLogOr{nodep->fileline(), newp, inewp} : inewp;
3464 : : }
3465 : : if (exprStmtp) VL_DO_DANGLING(exprStmtp->deleteTree(), exprStmtp);
3466 : : if (!newp) newp = new AstConst{nodep->fileline(), AstConst::BitFalse{}};
3467 : : UINFOTREE(9, newp, "", "inside-out");
3468 : : nodep->replaceWith(newp);
3469 : : VL_DO_DANGLING(pushDeletep(nodep), nodep);
3470 : : }
3471 : : AstNodeExpr* insideItem(AstNode* nodep, AstNodeExpr* exprp, AstNodeExpr* itemp) {
3472 : : const AstNodeDType* const itemDtp = itemp->dtypep()->skipRefp();
3473 : : if (AstInsideRange* const irangep = VN_CAST(itemp, InsideRange)) {
3474 : : // Similar logic in V3Case
3475 : : return irangep->newAndFromInside(exprp, irangep->lhsp()->unlinkFrBack(),
3476 : : irangep->rhsp()->unlinkFrBack());
3477 : : } else if (VN_IS(itemDtp, UnpackArrayDType) || VN_IS(itemDtp, DynArrayDType)
3478 : : || VN_IS(itemDtp, QueueDType)) {
3479 : : // Unsupported in parameters
3480 : : AstNodeExpr* const inewp = new AstCMethodHard{nodep->fileline(), itemp->unlinkFrBack(),
3481 : : VCMethod::ARRAY_INSIDE, exprp};
3482 : : iterateCheckTyped(nodep, "inside value", exprp, itemDtp->subDTypep(), BOTH);
3483 : : inewp->dtypeSetBit();
3484 : : inewp->didWidth(true);
3485 : : return inewp;
3486 : : } else if (VN_IS(itemDtp, AssocArrayDType)) {
3487 : : nodep->v3error("Inside operator not specified on associative arrays "
3488 : : "(IEEE 1800-2023 11.4.13)");
3489 : : VL_DO_DANGLING(exprp->deleteTree(), exprp);
3490 : : return nullptr;
3491 : : }
3492 : : return AstEqWild::newTyped(itemp->fileline(), exprp, itemp->unlinkFrBack());
3493 : : }
3494 : : void visit(AstInsideRange* nodep) override {
3495 : : // Just do each side; AstInside will rip these nodes out later
3496 : : userIterateAndNext(nodep->lhsp(), m_vup);
3497 : : userIterateAndNext(nodep->rhsp(), m_vup);
3498 : : nodep->dtypeFrom(nodep->lhsp());
3499 : : }
3500 : :
3501 : : void visit(AstIfaceRefDType* nodep) override {
3502 : : if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed
3503 : : UINFO(5, " IFACEREF " << nodep);
3504 : : userIterateChildren(nodep, m_vup);
3505 : : nodep->dtypep(nodep);
3506 : : UINFO(4, "dtWidthed " << nodep);
3507 : : }
3508 : : void visit(AstNodeUOrStructDType* nodep) override {
3509 : : if (nodep->doingWidth()) { // Early exit if have circular parameter definition
3510 : : nodep->v3error("Struct's type is circular: " << nodep->prettyName());
3511 : : nodep->dtypeSetBit();
3512 : : nodep->doingWidth(false);
3513 : : return;
3514 : : }
3515 : : if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed
3516 : : nodep->doingWidth(true);
3517 : : UINFO(5, " NODEUORS " << nodep);
3518 : : // Check for tagged unions
3519 : : if (const AstUnionDType* const unionp = VN_CAST(nodep, UnionDType)) {
3520 : : if (unionp->isTagged()) { nodep->v3warn(E_UNSUPPORTED, "Unsupported: tagged union"); }
3521 : : }
3522 : : // UINFOTREE(9, nodep, "", "class-in");
3523 : : if (!nodep->packed() && v3Global.opt.structsPacked()) nodep->packed(true);
3524 : : userIterateChildren(nodep, nullptr); // First size all members
3525 : : nodep->dtypep(nodep);
3526 : : nodep->isFourstate(false);
3527 : : // Error checks
3528 : : for (AstMemberDType* itemp = nodep->membersp(); itemp;
3529 : : itemp = VN_AS(itemp->nextp(), MemberDType)) {
3530 : : AstNodeDType* const dtp = itemp->subDTypep()->skipRefp();
3531 : : if (nodep->packed()
3532 : : && !dtp->isIntegralOrPacked()
3533 : : // Historically lax:
3534 : : && !v3Global.opt.structsPacked())
3535 : : itemp->v3error("Unpacked data type "
3536 : : << dtp->prettyDTypeNameQ()
3537 : : << " in packed struct/union (IEEE 1800-2023 7.2.1)");
3538 : : if ((VN_IS(nodep, UnionDType) || nodep->packed()) && itemp->valuep()) {
3539 : : itemp->v3error("Initial values not allowed in packed struct/union"
3540 : : " (IEEE 1800-2023 7.2.2)");
3541 : : pushDeletep(itemp->valuep()->unlinkFrBack());
3542 : : }
3543 : : }
3544 : : const bool isHardPackedUnion
3545 : : = nodep->packed() && VN_IS(nodep, UnionDType) && !VN_CAST(nodep, UnionDType)->isSoft();
3546 : :
3547 : : // Suppress union size errors in parameterized template modules where member
3548 : : // widths depend on unresolved parameters. Also suppress when the type has no
3549 : : // owning module (e.g. moved to TypeTable during DepGraph resolution).
3550 : : // TODO: Revisit this gate if DepGraph becomes the sole flow and widthing
3551 : : // can assume all types are already specialized.
3552 : : const bool inTemplateModule = (m_modep && m_modep->parameterizedTemplate())
3553 : : || (VN_IS(nodep, UnionDType) && !m_modep);
3554 : :
3555 : : // Determine bit assignments and width
3556 : : if (VN_IS(nodep, UnionDType) || nodep->packed()) {
3557 : : int lsb = 0;
3558 : : int width = 0;
3559 : : bool first = true;
3560 : : // Report errors on first member first
3561 : : AstMemberDType* itemp;
3562 : : // MSB is first, so loop backwards
3563 : : for (itemp = nodep->membersp(); itemp && itemp->nextp();
3564 : : itemp = VN_AS(itemp->nextp(), MemberDType)) {}
3565 : : for (; itemp; itemp = VN_CAST(itemp->backp(), MemberDType)) {
3566 : : if (itemp->isFourstate()) nodep->isFourstate(true);
3567 : : itemp->lsb(lsb);
3568 : : if (VN_IS(nodep, UnionDType)) {
3569 : : const int itemWidth = itemp->width();
3570 : : // Skip union size check for template modules with unresolved parameters
3571 : : if (!first && isHardPackedUnion && itemWidth != width && !inTemplateModule) {
3572 : : itemp->v3error("Hard packed union members must have equal size "
3573 : : "(IEEE 1800-2023 7.3.1)");
3574 : : }
3575 : : width = std::max(width, itemWidth);
3576 : : } else {
3577 : : lsb += itemp->width();
3578 : : width += itemp->width();
3579 : : }
3580 : : first = false;
3581 : : }
3582 : : nodep->widthForce(width, width); // Signing stays as-is, as parsed from declaration
3583 : : } else {
3584 : : nodep->widthForce(1, 1);
3585 : : }
3586 : : nodep->doingWidth(false);
3587 : : // UINFOTREE(9, nodep, "", "class-out");
3588 : : }
3589 : : void visit(AstThisRef* nodep) override {
3590 : : if (nodep->didWidthAndSet()) return;
3591 : : nodep->dtypep(iterateEditMoveDTypep(nodep, nodep->childDTypep()));
3592 : : if (m_ftaskp && m_ftaskp->isStatic()) {
3593 : : nodep->v3error("Cannot use 'this' in a static method "
3594 : : << m_ftaskp->prettyNameQ() << " (IEEE 1800-2023 8.10-8.11)");
3595 : : }
3596 : : }
3597 : : void visit(AstClassRefDType* nodep) override {
3598 : : if (nodep->didWidthAndSet()) return;
3599 : : // TODO this maybe eventually required to properly resolve members,
3600 : : // though causes problems with t_class_forward.v, so for now avoided
3601 : : // userIterateChildren(nodep->classp(), nullptr);
3602 : : }
3603 : : void visit(AstClassOrPackageRef* nodep) override {
3604 : : if (nodep->didWidthAndSet()) return;
3605 : : userIterateChildren(nodep, nullptr);
3606 : : }
3607 : : void visit(AstDot* nodep) override {
3608 : : // We can only reach this from constify called during V3Param (so before linkDotParam)
3609 : : // ... #(Cls#(...)::...) ...
3610 : : // ^^~~~ this is our DOT
3611 : : nodep->v3warn(E_UNSUPPORTED, "dotted expressions in parameters\n"
3612 : : << nodep->warnMore() << "... Suggest use a typedef");
3613 : : }
3614 : : void visit(AstClassExtends* nodep) override {
3615 : : if (nodep->didWidthAndSet()) return;
3616 : : if (VN_IS(nodep->childDTypep(), ClassRefDType)) {
3617 : : nodep->dtypep(iterateEditMoveDTypep(nodep, nodep->childDTypep()));
3618 : : }
3619 : : }
3620 : : void visit(AstMemberDType* nodep) override {
3621 : : if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed
3622 : : // Iterate into subDTypep() to resolve that type and update pointer.
3623 : : nodep->refDTypep(iterateEditMoveDTypep(nodep, nodep->subDTypep()));
3624 : : nodep->widthFromSub(nodep->subDTypep());
3625 : : nodep->dtypeFrom(nodep->subDTypep());
3626 : : if (nodep->valuep()) {
3627 : : userIterateAndNext(nodep->valuep(), WidthVP{nodep->dtypep(), PRELIM}.p());
3628 : : iterateCheckAssign(nodep, "Initial value", nodep->valuep(), FINAL, nodep->dtypep());
3629 : : }
3630 : : }
3631 : : void visit(AstStructSel* nodep) override {
3632 : : userIterateChildren(nodep, WidthVP{SELF, BOTH}.p());
3633 : : }
3634 : : void visit(AstMemberSel* nodep) override {
3635 : : UINFO(5, " MEMBERSEL " << nodep);
3636 : : if (nodep->didWidth()) return;
3637 : : VL_RESTORER(m_underMemberSel);
3638 : : m_underMemberSel = true;
3639 : : UINFOTREE(9, nodep, "", "mbs-in");
3640 : : userIterateChildren(nodep, WidthVP{SELF, BOTH}.p());
3641 : : UINFOTREE(9, nodep, "", "mbs-ic");
3642 : : // Find the fromp dtype - should be a class
3643 : : UASSERT_OBJ(nodep->fromp()->dtypep(), nodep->fromp(), "Unlinked data type");
3644 : : AstNodeDType* const fromDtp = nodep->fromp()->dtypep()->skipRefToEnump();
3645 : : UINFO(9, " from dt " << fromDtp);
3646 : : const AstMemberSel* const fromSel = VN_CAST(nodep->fromp(), MemberSel);
3647 : : if (AstClocking* const clockingp = fromSel && fromSel->varp()
3648 : : ? VN_CAST(fromSel->varp()->firstAbovep(), Clocking)
3649 : : : nullptr) {
3650 : : // In: MEMBERSEL{MEMBERSEL{vifaceref, "cb", cb_event}, "clockvar", null}
3651 : : // Out: MEMBERSEL{vifaceref, "clockvar", clockvar}
3652 : : UINFO(9, " from clocking " << clockingp);
3653 : : if (AstVar* const varp = memberSelClocking(nodep, clockingp)) {
3654 : : if (!varp->didWidth()) userIterate(varp, nullptr);
3655 : : AstMemberSel* fromp = VN_AS(nodep->fromp(), MemberSel);
3656 : : fromp->replaceWith(fromp->fromp()->unlinkFrBack());
3657 : : VL_DO_DANGLING(fromp->deleteTree(), fromp);
3658 : : nodep->dtypep(varp->dtypep());
3659 : : nodep->varp(varp);
3660 : : if (nodep->access().isWriteOrRW()) V3LinkLValue::linkLValueSet(nodep);
3661 : : if (AstIfaceRefDType* const adtypep
3662 : : = VN_CAST(nodep->fromp()->dtypep(), IfaceRefDType)) {
3663 : : nodep->varp()->sensIfacep(adtypep->ifacep());
3664 : : }
3665 : : UINFO(9, " done clocking msel " << nodep);
3666 : : nodep->didWidth(true); // Must not visit again: will confuse scopes
3667 : : return;
3668 : : }
3669 : : } else if (AstNodeUOrStructDType* const adtypep = VN_CAST(fromDtp, NodeUOrStructDType)) {
3670 : : if (memberSelStruct(nodep, adtypep)) return;
3671 : : } else if (AstClassRefDType* const adtypep = VN_CAST(fromDtp, ClassRefDType)) {
3672 : : if (memberSelClass(nodep, adtypep)) return;
3673 : : } else if (AstIfaceRefDType* const adtypep = VN_CAST(fromDtp, IfaceRefDType)) {
3674 : : if (AstNode* foundp = memberSelIface(nodep, adtypep)) {
3675 : : if (AstClocking* const clockingp = VN_CAST(foundp, Clocking))
3676 : : foundp = clockingp->ensureEventp();
3677 : : if (AstVar* const varp = VN_CAST(foundp, Var)) {
3678 : : if (!varp->didWidth()) userIterate(varp, nullptr);
3679 : : nodep->dtypep(foundp->dtypep());
3680 : : nodep->varp(varp);
3681 : : AstIface* const ifacep = adtypep->ifacep();
3682 : : varp->sensIfacep(ifacep);
3683 : : nodep->didWidth(true);
3684 : : return;
3685 : : }
3686 : : if (AstModport* const modportp = VN_CAST(foundp, Modport)) {
3687 : : // Modport selection (e.g. vif.passive_mp) is compile-time
3688 : : // type narrowing: replace MemberSel with fromp re-typed.
3689 : : AstIfaceRefDType* const newDtypep = new AstIfaceRefDType{
3690 : : nodep->fileline(), nodep->fileline(), adtypep->cellName(),
3691 : : adtypep->ifaceName(), modportp->name()};
3692 : : newDtypep->ifacep(adtypep->ifacep());
3693 : : newDtypep->cellp(adtypep->cellp());
3694 : : newDtypep->modportp(modportp);
3695 : : newDtypep->isVirtual(adtypep->isVirtual());
3696 : : v3Global.rootp()->typeTablep()->addTypesp(newDtypep);
3697 : : AstNodeExpr* const fromp = nodep->fromp()->unlinkFrBack();
3698 : : fromp->dtypep(newDtypep);
3699 : : nodep->replaceWith(fromp);
3700 : : VL_DO_DANGLING(pushDeletep(nodep), nodep);
3701 : : return;
3702 : : }
3703 : : if (AstCell* const cellp = VN_CAST(foundp, Cell)) {
3704 : : // Sub-interface cell selection (e.g. vif.tx): resolve to the
3705 : : // companion __Viftop var created by V3LinkCells for its dtype.
3706 : : if (VN_IS(cellp->modp(), Iface)) {
3707 : : const string viftopName = cellp->name() + "__Viftop";
3708 : : AstNodeModule* const parentIfacep = adtypep->ifaceViaCellp();
3709 : : AstVar* viftopVarp = nullptr;
3710 : : for (AstNode* itemp = parentIfacep->stmtsp(); itemp;
3711 : : itemp = itemp->nextp()) {
3712 : : if (AstVar* const vp = VN_CAST(itemp, Var)) {
3713 : : if (vp->name() == viftopName) {
3714 : : viftopVarp = vp;
3715 : : break;
3716 : : }
3717 : : }
3718 : : }
3719 : : UASSERT_OBJ(viftopVarp, nodep,
3720 : : "No __Viftop variable for sub-interface cell");
3721 : : if (!viftopVarp->didWidth()) userIterate(viftopVarp, nullptr);
3722 : : nodep->dtypep(viftopVarp->dtypep());
3723 : : nodep->varp(viftopVarp);
3724 : : viftopVarp->sensIfacep(VN_AS(cellp->modp(), Iface));
3725 : : nodep->didWidth(true);
3726 : : return;
3727 : : }
3728 : : }
3729 : : UINFO(1, "found object " << foundp);
3730 : : nodep->v3fatalSrc("MemberSel of non-variable\n"
3731 : : << nodep->warnContextPrimary() << '\n'
3732 : : << foundp->warnOther() << "... Location of found object\n"
3733 : : << foundp->warnContextSecondary());
3734 : : }
3735 : : } else if (VN_IS(fromDtp, EnumDType) //
3736 : : || VN_IS(fromDtp, AssocArrayDType) //
3737 : : || VN_IS(fromDtp, WildcardArrayDType) //
3738 : : || VN_IS(fromDtp, UnpackArrayDType) //
3739 : : || VN_IS(fromDtp, DynArrayDType) //
3740 : : || VN_IS(fromDtp, QueueDType) //
3741 : : || VN_IS(fromDtp, ConstraintRefDType) //
3742 : : || VN_IS(fromDtp, BasicDType)) {
3743 : : // Method call on enum without following parenthesis, e.g. "ENUM.next"
3744 : : // Convert this into a method call, and let that visitor figure out what to do next
3745 : : AstNode* const newp = new AstMethodCall{nodep->fileline(),
3746 : : nodep->fromp()->unlinkFrBack(), nodep->name()};
3747 : : nodep->replaceWith(newp);
3748 : : VL_DO_DANGLING(pushDeletep(nodep), nodep);
3749 : : userIterate(newp, m_vup);
3750 : : return;
3751 : : } else {
3752 : : nodep->v3error("Member selection of non-struct/union object '"
3753 : : << nodep->fromp()->prettyTypeName() << "' which is a '"
3754 : : << nodep->fromp()->dtypep()->prettyTypeName() << "'");
3755 : : }
3756 : : // Error handling
3757 : : nodep->replaceWith(new AstConst{nodep->fileline(), AstConst::BitFalse{}});
3758 : : VL_DO_DANGLING(pushDeletep(nodep), nodep);
3759 : : }
3760 : : bool memberSelClass(AstMemberSel* nodep, AstClassRefDType* adtypep) {
3761 : : if (nodep->name() == "rand_mode" || nodep->name() == "randomize") {
3762 : : AstMethodCall* const newp = new AstMethodCall{
3763 : : nodep->fileline(), nodep->fromp()->unlinkFrBack(), nodep->name()};
3764 : : nodep->replaceWith(newp);
3765 : : VL_DO_DANGLING(pushDeletep(nodep), nodep);
3766 : : visit(newp);
3767 : : return true;
3768 : : }
3769 : : // Returns true if ok
3770 : : // No need to width-resolve the class, as it was done when we did the child
3771 : : AstClass* const first_classp = adtypep->classp();
3772 : : UASSERT_OBJ(first_classp, nodep, "Unlinked");
3773 : : for (AstClass* classp = first_classp; classp;) {
3774 : : if (AstNode* const foundp = m_memberMap.findMember(classp, nodep->name())) {
3775 : : if (AstVar* const varp = VN_CAST(foundp, Var)) {
3776 : : if (!varp->didWidth()) userIterate(varp, nullptr);
3777 : : if (varp->lifetime().isStatic() || varp->isParam()) {
3778 : : // Static members are moved outside the class, so they shouldn't be
3779 : : // accessed by member select on a class object
3780 : : AstVarRef* const varRefp
3781 : : = new AstVarRef{nodep->fileline(), varp, nodep->access()};
3782 : : varRefp->classOrPackagep(classp);
3783 : : nodep->replaceWith(varRefp);
3784 : : VL_DO_DANGLING(pushDeletep(nodep), nodep);
3785 : : return true;
3786 : : }
3787 : : if (nodep->access().isWriteOrRW() && varp->isConst()) {
3788 : : nodep->v3warn(E_CONSTWRITTEN, "Writing to 'const' data-typed variable "
3789 : : << nodep->prettyNameQ()
3790 : : << " (IEEE 1800-2023 6.20.6)");
3791 : : }
3792 : : nodep->dtypep(foundp->dtypep());
3793 : : nodep->varp(varp);
3794 : : nodep->didWidth(true);
3795 : : if (nodep->fromp()->sameTree(m_randomizeFromp) && varp->isRand()) // null-safe
3796 : : V3LinkLValue::linkLValueSet(nodep);
3797 : : return true;
3798 : : }
3799 : : if (AstConstraint* constrp = VN_CAST(foundp, Constraint)) {
3800 : : nodep->replaceWith(new AstConstraintRef{
3801 : : nodep->fileline(), nodep->fromp()->unlinkFrBack(), constrp});
3802 : : VL_DO_DANGLING(pushDeletep(nodep), nodep);
3803 : : return true;
3804 : : }
3805 : : if (AstEnumItemRef* const adfoundp = VN_CAST(foundp, EnumItemRef)) {
3806 : : nodep->replaceWith(adfoundp->cloneTree(false));
3807 : : VL_DO_DANGLING(pushDeletep(nodep), nodep);
3808 : : return true;
3809 : : }
3810 : : if (AstNodeFTask* ftaskp = VN_CAST(foundp, NodeFTask)) {
3811 : : AstMethodCall* newp = new AstMethodCall{
3812 : : nodep->fileline(), nodep->fromp()->unlinkFrBack(), nodep->name()};
3813 : : newp->taskp(ftaskp);
3814 : : newp->dtypep(ftaskp->dtypep());
3815 : : newp->classOrPackagep(classp);
3816 : : nodep->replaceWith(newp);
3817 : : VL_DO_DANGLING(pushDeletep(nodep), nodep);
3818 : : return true;
3819 : : }
3820 : : if (VN_IS(foundp, Constraint)) {
3821 : : // We don't support constraints yet, so just keep as unlinked for now
3822 : : // Presumably we'll next see a constraint_mode AstMethodCall
3823 : : nodep->dtypep(nodep->findConstraintRefDType());
3824 : : UINFO(9, "Unsupported constraint select " << nodep);
3825 : : return true;
3826 : : }
3827 : : UINFO(1, "found object " << foundp);
3828 : : nodep->v3fatalSrc("MemberSel of non-variable\n"
3829 : : << nodep->warnContextPrimary() << '\n'
3830 : : << foundp->warnOther() << "... Location of found object\n"
3831 : : << foundp->warnContextSecondary());
3832 : : }
3833 : : classp = classp->extendsp() ? classp->extendsp()->classp() : nullptr;
3834 : : }
3835 : :
3836 : : VSpellCheck speller;
3837 : : for (AstClass* classp = first_classp; classp;) {
3838 : : for (AstNode* itemp = classp->membersp(); itemp; itemp = itemp->nextp()) {
3839 : : if (VN_IS(itemp, Constraint) || VN_IS(itemp, EnumItemRef) || VN_IS(itemp, Var)) {
3840 : : speller.pushCandidate(itemp->prettyName());
3841 : : }
3842 : : }
3843 : : classp = classp->extendsp() ? classp->extendsp()->classp() : nullptr;
3844 : : }
3845 : : const string suggest = speller.bestCandidateMsg(nodep->prettyName());
3846 : : nodep->v3error(
3847 : : "Member " << nodep->prettyNameQ() << " not found in " << first_classp->verilogKwd()
3848 : : << " " << first_classp->prettyNameQ() << "\n"
3849 : : << (suggest.empty() ? "" : nodep->fileline()->warnMore() + suggest));
3850 : : return false; // Caller handles error
3851 : : }
3852 : : AstNode* memberSelIface(AstMemberSel* nodep, AstIfaceRefDType* adtypep) {
3853 : : // Returns node if ok
3854 : : // No need to width-resolve the interface, as it was done when we did the child
3855 : : // ifaceViaCellp() handles dtypes with cellp-only (no ifacep), as produced
3856 : : // by sub-interface selection, enabling chained access (e.g. vif.tx.Tx).
3857 : : AstNodeModule* const ifacep = adtypep->ifaceViaCellp();
3858 : : UASSERT_OBJ(ifacep, nodep, "Unlinked");
3859 : : VSpellCheck speller;
3860 : : for (AstNode* itemp = ifacep->stmtsp(); itemp; itemp = itemp->nextp()) {
3861 : : if (itemp->name() == nodep->name()) return itemp;
3862 : : if (VN_IS(itemp, Var) || VN_IS(itemp, Modport) || VN_IS(itemp, Cell)) {
3863 : : speller.pushCandidate(itemp->prettyName());
3864 : : }
3865 : : }
3866 : : const string suggest = speller.bestCandidateMsg(nodep->prettyName());
3867 : : nodep->v3error(
3868 : : "Member " << nodep->prettyNameQ() << " not found in interface "
3869 : : << ifacep->prettyNameQ() << "\n"
3870 : : << (suggest.empty() ? "" : nodep->fileline()->warnMore() + suggest));
3871 : : return nullptr; // Caller handles error
3872 : : }
3873 : : AstVar* memberSelClocking(AstMemberSel* nodep, AstClocking* clockingp) {
3874 : : // Returns node if ok
3875 : : VSpellCheck speller;
3876 : :
3877 : : for (AstNode* itemp = clockingp->itemsp(); itemp; itemp = itemp->nextp()) {
3878 : : if (AstClockingItem* citemp = VN_CAST(itemp, ClockingItem)) {
3879 : : if (citemp->varp()->name() == nodep->name()) return citemp->varp();
3880 : : speller.pushCandidate(citemp->varp()->prettyName());
3881 : : }
3882 : : }
3883 : : const string suggest = speller.bestCandidateMsg(nodep->prettyName());
3884 : : nodep->v3error(
3885 : : "Member " << nodep->prettyNameQ() << " not found in clocking block "
3886 : : << clockingp->prettyNameQ() << "\n"
3887 : : << (suggest.empty() ? "" : nodep->fileline()->warnMore() + suggest));
3888 : : return nullptr; // Caller handles error
3889 : : }
3890 : : bool memberSelStruct(AstMemberSel* nodep, AstNodeUOrStructDType* adtypep) {
3891 : : // Returns true if ok
3892 : : if (AstMemberDType* const memberp
3893 : : = VN_CAST(m_memberMap.findMember(adtypep, nodep->name()), MemberDType)) {
3894 : : if (m_attrp) { // Looking for the base of the attribute
3895 : : nodep->dtypep(memberp);
3896 : : UINFO(9, " MEMBERSEL(attr) -> " << nodep);
3897 : : UINFO(9, " dt-> " << nodep->dtypep());
3898 : : } else if (adtypep->packed()) {
3899 : : AstSel* const newp = new AstSel{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
3900 : : memberp->lsb(), memberp->width()};
3901 : : // Must skip over the member to find the union; as the member may disappear later
3902 : : newp->dtypep(memberp->subDTypep()->skipRefToEnump());
3903 : : newp->didWidth(true); // Don't replace dtype with basic type
3904 : : UINFO(9, " MEMBERSEL -> " << newp);
3905 : : UINFO(9, " dt-> " << newp->dtypep());
3906 : : nodep->replaceWith(newp);
3907 : : VL_DO_DANGLING(pushDeletep(nodep), nodep);
3908 : : // Should be able to treat it as a normal-ish nodesel - maybe.
3909 : : // The lhsp() will be strange until this stage; create the number here?
3910 : : } else {
3911 : : AstStructSel* const newp = new AstStructSel{
3912 : : nodep->fileline(), nodep->fromp()->unlinkFrBack(), nodep->name()};
3913 : : // Must skip over the member to find the union; as the member may disappear later
3914 : : newp->dtypep(memberp->subDTypep()->skipRefToEnump());
3915 : : newp->didWidth(true); // Don't replace dtype with basic type
3916 : : UINFO(9, " MEMBERSEL -> " << newp);
3917 : : UINFO(9, " dt-> " << newp->dtypep());
3918 : : nodep->replaceWith(newp);
3919 : : VL_DO_DANGLING(pushDeletep(nodep), nodep);
3920 : : // Should be able to treat it as a normal-ish nodesel - maybe.
3921 : : // The lhsp() will be strange until this stage; create the number here?
3922 : : }
3923 : : return true;
3924 : : }
3925 : : nodep->v3error("Member " << nodep->prettyNameQ() << " not found in structure");
3926 : : return false;
3927 : : }
3928 : :
3929 : : void visit(AstCMethodHard* nodep) override {
3930 : : // Never created before V3Width, so no need to redo it
3931 : : UASSERT_OBJ(nodep->dtypep(), nodep, "CMETHODCALLs should have already been sized");
3932 : : }
3933 : :
3934 : : void visit(AstMethodCall* nodep) override {
3935 : : UINFO(5, " METHODCALL " << nodep);
3936 : : if (nodep->didWidth()) return;
3937 : : UINFOTREE(9, nodep, "", "mts-in");
3938 : : // Should check types the method requires, but at present we don't do much
3939 : : userIterate(nodep->fromp(), WidthVP{SELF, BOTH}.p());
3940 : : // Args are checked within each particular method's decode
3941 : : // Any AstWith is checked later when know types, in methodWithClause
3942 : : // Find the fromp dtype - should be a class
3943 : : UASSERT_OBJ(nodep->fromp() && nodep->fromp()->dtypep(), nodep, "Unsized expression");
3944 : : AstNodeDType* const fromDtp = nodep->fromp()->dtypep()->skipRefToEnump();
3945 : : AstBasicDType* const basicp = fromDtp ? fromDtp->basicp() : nullptr;
3946 : : UINFO(9, " from dt " << fromDtp);
3947 : : userIterate(fromDtp, WidthVP{SELF, BOTH}.p());
3948 : : if (nodep->name() == "rand_mode") {
3949 : : methodCallRandMode(nodep);
3950 : : } else if (nodep->name() == "constraint_mode") {
3951 : : methodCallConstraint(nodep, nullptr);
3952 : : } else if (AstEnumDType* const adtypep = VN_CAST(fromDtp, EnumDType)) {
3953 : : methodCallEnum(nodep, adtypep);
3954 : : } else if (AstAssocArrayDType* const adtypep = VN_CAST(fromDtp, AssocArrayDType)) {
3955 : : methodCallAssoc(nodep, adtypep);
3956 : : } else if (AstWildcardArrayDType* const adtypep = VN_CAST(fromDtp, WildcardArrayDType)) {
3957 : : methodCallWildcard(nodep, adtypep);
3958 : : } else if (AstDynArrayDType* const adtypep = VN_CAST(fromDtp, DynArrayDType)) {
3959 : : methodCallDyn(nodep, adtypep);
3960 : : } else if (AstQueueDType* const adtypep = VN_CAST(fromDtp, QueueDType)) {
3961 : : methodCallQueue(nodep, adtypep);
3962 : : } else if (AstClassRefDType* const adtypep = VN_CAST(fromDtp, ClassRefDType)) {
3963 : : methodCallClass(nodep, adtypep);
3964 : : } else if (AstIfaceRefDType* const adtypep = VN_CAST(fromDtp, IfaceRefDType)) {
3965 : : methodCallIfaceRef(nodep, adtypep);
3966 : : } else if (AstUnpackArrayDType* const adtypep = VN_CAST(fromDtp, UnpackArrayDType)) {
3967 : : methodCallUnpack(nodep, adtypep);
3968 : : } else if (AstConstraintRefDType* const adtypep = VN_CAST(fromDtp, ConstraintRefDType)) {
3969 : : methodCallConstraint(nodep, adtypep);
3970 : : } else if (basicp && basicp->isEvent()) {
3971 : : methodCallEvent(nodep, basicp);
3972 : : } else if (basicp && basicp->isString()) {
3973 : : methodCallString(nodep, basicp);
3974 : : } else {
3975 : : nodep->v3warn(E_UNSUPPORTED, "Unsupported: Member call on object '"
3976 : : << nodep->fromp()->prettyTypeName()
3977 : : << "' which is a '"
3978 : : << nodep->fromp()->dtypep()->prettyTypeName() << "'");
3979 : : nodep->dtypeSetVoid();
3980 : : }
3981 : : }
3982 : : AstWith* methodWithClause(AstNodeFTaskRef* nodep, bool required, bool arbReturn,
3983 : : AstNodeDType* returnDtp, AstNodeDType* indexDtp,
3984 : : AstNodeDType* valueDtp) {
3985 : : UASSERT_OBJ(arbReturn || returnDtp, nodep, "Null return type");
3986 : : if (AstWith* const withp = nodep->withp()) {
3987 : : withp->indexArgRefp()->dtypep(indexDtp);
3988 : : withp->valueArgRefp()->dtypep(valueDtp);
3989 : : userIterate(withp, WidthVP{returnDtp, BOTH}.p());
3990 : : withp->unlinkFrBack();
3991 : : return withp;
3992 : : }
3993 : : if (required) {
3994 : : nodep->v3error("'with' statement is required for ." << nodep->prettyName()
3995 : : << " method");
3996 : : }
3997 : : return nullptr;
3998 : : }
3999 : : void methodOkArguments(AstNodeFTaskRef* nodep, int minArg, int maxArg) {
4000 : : int narg = 0;
4001 : : if (AstWith* const withp = nodep->withp()) {
4002 : : withp->v3error("'with' not legal on this method");
4003 : : VL_DO_DANGLING(pushDeletep(withp->unlinkFrBack()), withp);
4004 : : }
4005 : : for (AstArg* argp = nodep->argsp(); argp; argp = VN_AS(argp->nextp(), Arg)) ++narg;
4006 : : const bool ok = (narg >= minArg) && (narg <= maxArg);
4007 : : if (!ok) {
4008 : : nodep->v3error("The " << narg << " arguments passed to ." << nodep->prettyName()
4009 : : << " method does not match its requiring " << cvtToStr(minArg)
4010 : : << (minArg == maxArg ? "" : " to " + cvtToStr(maxArg))
4011 : : << " arguments");
4012 : : // Adjust to required argument counts, very bogus, but avoids core dump
4013 : : for (; narg < minArg; ++narg) {
4014 : : nodep->addArgsp(
4015 : : new AstArg{nodep->fileline(), "", new AstConst(nodep->fileline(), 0)});
4016 : : }
4017 : : for (; narg > maxArg; --narg) {
4018 : : AstArg* argp = nodep->argsp();
4019 : : while (argp->nextp()) argp = VN_AS(argp->nextp(), Arg);
4020 : : argp->unlinkFrBack();
4021 : : VL_DO_DANGLING(argp->deleteTree(), argp);
4022 : : }
4023 : : }
4024 : : }
4025 : :
4026 : : AstNodeExpr* methodArg(AstMethodCall* nodep, int arg) {
4027 : : AstArg* argp = nodep->argsp();
4028 : : for (int narg = 0; narg < arg; ++narg) argp = VN_AS(argp->nextp(), Arg);
4029 : : UASSERT_OBJ(argp, nodep, "methodOkArguments() should have detected arg count error");
4030 : : return argp->exprp();
4031 : : }
4032 : :
4033 : : void methodCallEnum(AstMethodCall* nodep, AstEnumDType* adtypep) {
4034 : : // Method call on enum without following parenthesis, e.g. "ENUM.next"
4035 : : // Convert this into a method call, and let that visitor figure out what to do next
4036 : : if (nodep->name() == "num" //
4037 : : || nodep->name() == "first" //
4038 : : || nodep->name() == "last") {
4039 : : // Constant value
4040 : : AstConst* newp = nullptr;
4041 : : methodOkArguments(nodep, 0, 0);
4042 : : if (nodep->name() == "num") {
4043 : : int items = 0;
4044 : : for (AstNode* itemp = adtypep->itemsp(); itemp; itemp = itemp->nextp()) ++items;
4045 : : newp = new AstConst(nodep->fileline(), AstConst::Signed32{}, items);
4046 : : } else if (nodep->name() == "first") {
4047 : : const AstEnumItem* itemp = adtypep->itemsp();
4048 : : if (!itemp) {
4049 : : newp = new AstConst(nodep->fileline(), AstConst::Signed32{},
4050 : : 0); // Spec doesn't say what to do
4051 : : } else {
4052 : : newp = VN_AS(itemp->valuep()->cloneTree(false), Const); // A const
4053 : : newp->dtypeFrom(adtypep); // To prevent a later ENUMVALUE
4054 : : }
4055 : : } else if (nodep->name() == "last") {
4056 : : const AstEnumItem* itemp = adtypep->itemsp();
4057 : : while (itemp && itemp->nextp()) itemp = VN_AS(itemp->nextp(), EnumItem);
4058 : : if (!itemp) {
4059 : : newp = new AstConst(nodep->fileline(), AstConst::Signed32{},
4060 : : 0); // Spec doesn't say what to do
4061 : : } else {
4062 : : newp = VN_AS(itemp->valuep()->cloneTree(false), Const); // A const
4063 : : newp->dtypeFrom(adtypep); // To prevent a later ENUMVALUE
4064 : : }
4065 : : }
4066 : : UASSERT_OBJ(newp, nodep, "Enum method (perhaps enum item) not const");
4067 : : newp->fileline(nodep->fileline()); // Use method's filename/line number to be clearer;
4068 : : // may have warning disables
4069 : : nodep->replaceWith(newp);
4070 : : VL_DO_DANGLING(pushDeletep(nodep), nodep);
4071 : : } else if (nodep->name() == "name" || nodep->name() == "next" || nodep->name() == "prev") {
4072 : : VAttrType attrType;
4073 : : if (nodep->name() == "name") {
4074 : : attrType = VAttrType::ENUM_NAME;
4075 : : methodOkArguments(nodep, 0, 0);
4076 : : } else if (nodep->name() == "next") {
4077 : : attrType = VAttrType::ENUM_NEXT;
4078 : : methodOkArguments(nodep, 0, 1);
4079 : : } else if (nodep->name() == "prev") {
4080 : : attrType = VAttrType::ENUM_PREV;
4081 : : methodOkArguments(nodep, 0, 1);
4082 : : } else {
4083 : : nodep->v3fatalSrc("Bad case");
4084 : : }
4085 : :
4086 : : if (nodep->name() != "name" && nodep->argsp()) {
4087 : : AstNodeExpr* stepp = methodArg(nodep, 0);
4088 : : VL_DO_DANGLING(V3Const::constifyParamsNoWarnEdit(stepp), stepp);
4089 : : stepp = methodArg(nodep, 0);
4090 : : nodep->fileline()->modifyWarnOff(V3ErrorCode::WIDTHEXPAND, true);
4091 : : iterateCheckUInt32(nodep, "argument", stepp, BOTH);
4092 : : if (!VN_IS(stepp, Const)) {
4093 : : stepp->v3warn(E_UNSUPPORTED,
4094 : : "Unsupported: enum next/prev with non-constant argument");
4095 : : AstNodeExpr* const newp = new AstConst{stepp->fileline(), 1};
4096 : : stepp->replaceWith(newp);
4097 : : VL_DO_DANGLING(pushDeletep(stepp), stepp);
4098 : : stepp = newp;
4099 : : }
4100 : : const uint32_t stepWidth = VN_AS(stepp, Const)->toUInt();
4101 : : if (stepWidth == 0) {
4102 : : // Step of 0 "legalizes" like $cast, use .next.prev
4103 : : AstMethodCall* const newp = new AstMethodCall{
4104 : : nodep->fileline(),
4105 : : new AstMethodCall{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
4106 : : "next"},
4107 : : "prev"};
4108 : : // No dtype assigned, we will recurse the new method and replace
4109 : : nodep->replaceWith(newp);
4110 : : VL_DO_DANGLING(nodep->deleteTree(), nodep);
4111 : : return;
4112 : : } else if (stepWidth != 1) {
4113 : : // Unroll of enumVar.next(k) to enumVar.next(1).next(k - 1)
4114 : : pushDeletep(nodep->argsp()->unlinkFrBack());
4115 : : AstMethodCall* const clonep = nodep->cloneTree(false);
4116 : : VN_AS(stepp, Const)->num().setLong(1);
4117 : : AstConst* const constp = new AstConst(nodep->fileline(), stepWidth - 1);
4118 : : AstArg* const argp = new AstArg{nodep->fileline(), "", constp};
4119 : : AstMethodCall* const newp
4120 : : = new AstMethodCall{nodep->fileline(), clonep, nodep->name(), argp};
4121 : : // No dtype assigned, we will recurse the new method and replace
4122 : : nodep->replaceWith(newp);
4123 : : VL_DO_DANGLING(nodep->deleteTree(), nodep);
4124 : : return;
4125 : : }
4126 : : }
4127 : : AstNodeExpr* const newp
4128 : : = enumSelect(nodep->fromp()->unlinkFrBack(), adtypep, attrType);
4129 : : nodep->replaceWith(newp);
4130 : : VL_DO_DANGLING(nodep->deleteTree(), nodep);
4131 : : } else {
4132 : : nodep->v3error("Unknown built-in enum method " << nodep->prettyNameQ());
4133 : : }
4134 : : }
4135 : : void methodCallWildcard(AstMethodCall* nodep, AstWildcardArrayDType* adtypep) {
4136 : : AstCMethodHard* newp = nullptr;
4137 : : if (nodep->name() == "num" // function int num()
4138 : : || nodep->name() == "size") {
4139 : : methodOkArguments(nodep, 0, 0);
4140 : : newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
4141 : : VCMethod::ASSOC_SIZE}; // So don't need num()
4142 : : newp->dtypeSetSigned32();
4143 : : } else if (nodep->name() == "first" // function int first(ref index)
4144 : : || nodep->name() == "last" //
4145 : : || nodep->name() == "next" //
4146 : : || nodep->name() == "prev" //
4147 : : || nodep->name() == "unique_index" //
4148 : : || nodep->name() == "find_index" || nodep->name() == "find_first_index"
4149 : : || nodep->name() == "find_last_index") {
4150 : : nodep->v3error("Array method " << nodep->prettyNameQ()
4151 : : << " not legal on wildcard associative arrays");
4152 : : } else if (nodep->name() == "exists") { // function int exists(input index)
4153 : : // IEEE really should have made this a "bit" return
4154 : : methodOkArguments(nodep, 1, 1);
4155 : : iterateCheckSelf(nodep, "argument", methodArg(nodep, 0), SELF, BOTH);
4156 : : AstNodeExpr* const index_exprp = methodCallWildcardIndexExpr(nodep, adtypep);
4157 : : newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
4158 : : VCMethod::ASSOC_EXISTS, index_exprp->unlinkFrBack()};
4159 : : newp->dtypeSetLogicUnsized(32, 1, VSigning::SIGNED);
4160 : : } else if (nodep->name() == "delete") { // function void delete([input integer index])
4161 : : methodOkArguments(nodep, 0, 1);
4162 : : methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::WRITE);
4163 : : if (!nodep->argsp()) {
4164 : : newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
4165 : : VCMethod::ASSOC_CLEAR};
4166 : : newp->dtypeSetVoid();
4167 : : } else {
4168 : : iterateCheckSelf(nodep, "argument", methodArg(nodep, 0), SELF, BOTH);
4169 : : AstNodeExpr* const index_exprp = methodCallWildcardIndexExpr(nodep, adtypep);
4170 : : newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
4171 : : VCMethod::ASSOC_ERASE, index_exprp->unlinkFrBack()};
4172 : : newp->dtypeSetVoid();
4173 : : }
4174 : : } else if (nodep->name() == "sort" || nodep->name() == "rsort"
4175 : : || nodep->name() == "reverse" || nodep->name() == "shuffle") {
4176 : : nodep->v3error("Array method " << nodep->prettyNameQ()
4177 : : << " not legal on associative arrays");
4178 : : } else if (nodep->name() == "and" || nodep->name() == "or" || nodep->name() == "xor"
4179 : : || nodep->name() == "sum" || nodep->name() == "product") {
4180 : : // All value return
4181 : : AstWith* const withp
4182 : : = methodWithClause(nodep, false, false, adtypep->subDTypep(),
4183 : : adtypep->findStringDType(), adtypep->subDTypep());
4184 : : methodOkArguments(nodep, 0, 0);
4185 : : methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::READ);
4186 : : newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
4187 : : VCMethod::arrayMethod("r_" + nodep->name())};
4188 : : newp->withp(withp);
4189 : : newp->dtypeFrom(withp ? withp->dtypep() : adtypep->subDTypep());
4190 : : if (!nodep->firstAbovep()) newp->dtypeSetVoid();
4191 : : } else if (nodep->name() == "min" || nodep->name() == "max" || nodep->name() == "unique") {
4192 : : methodOkArguments(nodep, 0, 0);
4193 : : methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::READ);
4194 : : newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
4195 : : VCMethod::arrayMethod(nodep->name())};
4196 : : newp->dtypep(queueDTypeIndexedBy(adtypep->subDTypep()));
4197 : : if (!nodep->firstAbovep()) newp->dtypeSetVoid();
4198 : : } else if (nodep->name() == "find" || nodep->name() == "find_first"
4199 : : || nodep->name() == "find_last") {
4200 : : AstWith* const withp
4201 : : = methodWithClause(nodep, true, false, nodep->findBitDType(),
4202 : : adtypep->findStringDType(), adtypep->subDTypep());
4203 : : methodOkArguments(nodep, 0, 0);
4204 : : methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::READ);
4205 : : newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
4206 : : VCMethod::arrayMethod(nodep->name())};
4207 : : newp->withp(withp);
4208 : : newp->dtypep(queueDTypeIndexedBy(adtypep->subDTypep()));
4209 : : if (!nodep->firstAbovep()) newp->dtypeSetVoid();
4210 : : } else if (nodep->name() == "map") {
4211 : : AstWith* const withp
4212 : : = methodWithClause(nodep, true, false, adtypep->subDTypep(),
4213 : : adtypep->findStringDType(), adtypep->subDTypep());
4214 : : methodOkArguments(nodep, 0, 0);
4215 : : methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::READ);
4216 : : newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
4217 : : VCMethod::ARRAY_MAP};
4218 : : newp->withp(withp);
4219 : : newp->dtypep(queueDTypeIndexedBy(withp ? withp->dtypep() : adtypep->subDTypep()));
4220 : : if (!nodep->firstAbovep()) newp->dtypeSetVoid();
4221 : : } else {
4222 : : nodep->v3error("Unknown wildcard associative array method " << nodep->prettyNameQ());
4223 : : nodep->dtypeFrom(adtypep->subDTypep()); // Best guess
4224 : : }
4225 : : if (newp) {
4226 : : newp->protect(false);
4227 : : newp->didWidth(true);
4228 : : nodep->replaceWith(newp);
4229 : : VL_DO_DANGLING(nodep->deleteTree(), nodep);
4230 : : }
4231 : : }
4232 : : void methodCallAssoc(AstMethodCall* nodep, AstAssocArrayDType* adtypep) {
4233 : : AstCMethodHard* newp = nullptr;
4234 : : if (nodep->name() == "num" // function int num()
4235 : : || nodep->name() == "size") {
4236 : : methodOkArguments(nodep, 0, 0);
4237 : : newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
4238 : : VCMethod::ASSOC_SIZE}; // So don't need num()
4239 : : newp->dtypeSetSigned32();
4240 : : } else if (nodep->name() == "first" // function int first(ref index)
4241 : : || nodep->name() == "last" //
4242 : : || nodep->name() == "next" //
4243 : : || nodep->name() == "prev") {
4244 : : methodOkArguments(nodep, 1, 1);
4245 : : iterateCheckTyped(nodep, "argument", methodArg(nodep, 0), adtypep->keyDTypep(), BOTH);
4246 : : AstNodeExpr* const index_exprp = methodCallAssocIndexExpr(nodep, adtypep);
4247 : : methodCallLValueRecurse(nodep, index_exprp, VAccess::READWRITE);
4248 : : newp
4249 : : = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
4250 : : VCMethod::arrayMethod(nodep->name()), // first/last/next/prev
4251 : : index_exprp->unlinkFrBack()};
4252 : : newp->dtypeSetSigned32();
4253 : : if (!nodep->firstAbovep()) newp->dtypeSetVoid();
4254 : : } else if (nodep->name() == "exists") { // function int exists(input index)
4255 : : // IEEE really should have made this a "bit" return
4256 : : methodOkArguments(nodep, 1, 1);
4257 : : iterateCheckTyped(nodep, "argument", methodArg(nodep, 0), adtypep->keyDTypep(), BOTH);
4258 : : AstNodeExpr* const index_exprp = methodCallAssocIndexExpr(nodep, adtypep);
4259 : : newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
4260 : : VCMethod::ASSOC_EXISTS, index_exprp->unlinkFrBack()};
4261 : : newp->dtypeSetLogicUnsized(32, 1, VSigning::SIGNED);
4262 : : } else if (nodep->name() == "delete") { // function void delete([input integer index])
4263 : : methodOkArguments(nodep, 0, 1);
4264 : : methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::WRITE);
4265 : : if (!nodep->argsp()) {
4266 : : newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
4267 : : VCMethod::ASSOC_CLEAR};
4268 : : newp->dtypeSetVoid();
4269 : : } else {
4270 : : iterateCheckTyped(nodep, "argument", methodArg(nodep, 0), adtypep->keyDTypep(),
4271 : : BOTH);
4272 : : AstNodeExpr* const index_exprp = methodCallAssocIndexExpr(nodep, adtypep);
4273 : : newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
4274 : : VCMethod::ASSOC_ERASE, index_exprp->unlinkFrBack()};
4275 : : newp->dtypeSetVoid();
4276 : : }
4277 : : } else if (nodep->name() == "sort" || nodep->name() == "rsort"
4278 : : || nodep->name() == "reverse" || nodep->name() == "shuffle") {
4279 : : nodep->v3error("Array method " << nodep->prettyNameQ()
4280 : : << " not legal on associative arrays");
4281 : : } else if (nodep->name() == "and" || nodep->name() == "or" || nodep->name() == "xor"
4282 : : || nodep->name() == "sum" || nodep->name() == "product") {
4283 : : // All value return
4284 : : AstWith* const withp = methodWithClause(nodep, false, false, adtypep->subDTypep(),
4285 : : adtypep->keyDTypep(), adtypep->subDTypep());
4286 : : methodOkArguments(nodep, 0, 0);
4287 : : methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::READ);
4288 : : newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
4289 : : VCMethod::arrayMethod("r_" + nodep->name())};
4290 : : newp->withp(withp);
4291 : : newp->dtypeFrom(withp ? withp->dtypep() : adtypep->subDTypep());
4292 : : if (!nodep->firstAbovep()) newp->dtypeSetVoid();
4293 : : } else if (nodep->name() == "min" || nodep->name() == "max" || nodep->name() == "unique"
4294 : : || nodep->name() == "unique_index") {
4295 : : AstWith* const withp = methodWithClause(
4296 : : nodep, false, true, nullptr, nodep->findUInt32DType(), adtypep->subDTypep());
4297 : : methodOkArguments(nodep, 0, 0);
4298 : : methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::READ);
4299 : : newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
4300 : : VCMethod::arrayMethod(nodep->name())};
4301 : : newp->withp(withp);
4302 : : if (nodep->name() == "unique_index") {
4303 : : newp->dtypep(queueDTypeIndexedBy(adtypep->keyDTypep()));
4304 : : } else {
4305 : : newp->dtypep(queueDTypeIndexedBy(adtypep->subDTypep()));
4306 : : }
4307 : : if (!nodep->firstAbovep()) newp->dtypeSetVoid();
4308 : : } else if (nodep->name() == "find" || nodep->name() == "find_first"
4309 : : || nodep->name() == "find_last") {
4310 : : AstWith* const withp = methodWithClause(nodep, true, false, nodep->findBitDType(),
4311 : : adtypep->keyDTypep(), adtypep->subDTypep());
4312 : : methodOkArguments(nodep, 0, 0);
4313 : : methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::READ);
4314 : : newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
4315 : : VCMethod::arrayMethod(nodep->name())};
4316 : : newp->withp(withp);
4317 : : newp->dtypep(queueDTypeIndexedBy(adtypep->subDTypep()));
4318 : : if (!nodep->firstAbovep()) newp->dtypeSetVoid();
4319 : : } else if (nodep->name() == "find_index" || nodep->name() == "find_first_index"
4320 : : || nodep->name() == "find_last_index") {
4321 : : AstWith* const withp = methodWithClause(nodep, true, false, nodep->findBitDType(),
4322 : : adtypep->keyDTypep(), adtypep->subDTypep());
4323 : : methodOkArguments(nodep, 0, 0);
4324 : : methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::READ);
4325 : : newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
4326 : : VCMethod::arrayMethod(nodep->name())};
4327 : : newp->withp(withp);
4328 : : newp->dtypep(queueDTypeIndexedBy(adtypep->keyDTypep()));
4329 : : if (!nodep->firstAbovep()) newp->dtypeSetVoid();
4330 : : } else if (nodep->name() == "map") {
4331 : : AstWith* const withp = methodWithClause(nodep, true, false, adtypep->subDTypep(),
4332 : : adtypep->keyDTypep(), adtypep->subDTypep());
4333 : : methodOkArguments(nodep, 0, 0);
4334 : : methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::READ);
4335 : : newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
4336 : : VCMethod::ARRAY_MAP};
4337 : : newp->withp(withp);
4338 : : newp->dtypep(queueDTypeIndexedBy(withp ? withp->dtypep() : adtypep->subDTypep()));
4339 : : if (!nodep->firstAbovep()) newp->dtypeSetVoid();
4340 : : } else {
4341 : : nodep->v3error("Unknown built-in associative array method " << nodep->prettyNameQ());
4342 : : nodep->dtypeFrom(adtypep->subDTypep()); // Best guess
4343 : : }
4344 : : if (newp) {
4345 : : newp->protect(false);
4346 : : newp->didWidth(true);
4347 : : nodep->replaceWith(newp);
4348 : : VL_DO_DANGLING(nodep->deleteTree(), nodep);
4349 : : }
4350 : : }
4351 : : AstNodeExpr* methodCallAssocIndexExpr(AstMethodCall* nodep, AstAssocArrayDType* adtypep) {
4352 : : AstNode* const index_exprp = nodep->argsp()->exprp();
4353 : : iterateCheck(nodep, "index", index_exprp, CONTEXT_DET, FINAL, adtypep->keyDTypep(),
4354 : : EXTEND_EXP);
4355 : : VL_DANGLING(index_exprp); // May have been edited
4356 : : return nodep->argsp()->exprp();
4357 : : }
4358 : : AstNodeExpr* methodCallWildcardIndexExpr(AstMethodCall* nodep,
4359 : : AstWildcardArrayDType* adtypep) {
4360 : : AstNode* const index_exprp = nodep->argsp()->exprp();
4361 : : iterateCheck(nodep, "index", index_exprp, CONTEXT_DET, FINAL, adtypep->findStringDType(),
4362 : : EXTEND_EXP);
4363 : : VL_DANGLING(index_exprp); // May have been edited
4364 : : return nodep->argsp()->exprp();
4365 : : }
4366 : : void methodCallLValueRecurse(AstMethodCall* nodep, AstNode* childp, const VAccess& access) {
4367 : : if (AstCMethodHard* const ichildp = VN_CAST(childp, CMethodHard)) {
4368 : : bool methAt = false;
4369 : : bool methAtBack = false;
4370 : : bool methAtWrite = false;
4371 : : switch (ichildp->method()) {
4372 : : case VCMethod::ARRAY_AT: // FALLTHRU
4373 : : methAt = true;
4374 : : break;
4375 : : case VCMethod::ARRAY_AT_BACK: // FALLTHRU
4376 : : methAtBack = true;
4377 : : break;
4378 : : case VCMethod::ARRAY_AT_WRITE: // FALLTHRU
4379 : : case VCMethod::DYN_AT_WRITE_APPEND: // FALLTHRU
4380 : : case VCMethod::DYN_AT_WRITE_APPEND_BACK: //
4381 : : methAtWrite = true;
4382 : : break;
4383 : : default: break;
4384 : : }
4385 : : if (methAt || methAtBack || methAtWrite) {
4386 : : const AstNodeDType* const fromDtypep = ichildp->fromp()->dtypep()->skipRefp();
4387 : : if (VN_IS(fromDtypep, QueueDType) || VN_IS(fromDtypep, DynArrayDType)) {
4388 : : // Change access methods to writable ones
4389 : : if (methAt) {
4390 : : ichildp->method(VCMethod::ARRAY_AT_WRITE);
4391 : : } else if (methAtBack) {
4392 : : ichildp->method(VCMethod::DYN_AT_WRITE_APPEND_BACK);
4393 : : }
4394 : : }
4395 : : methodCallLValueRecurse(nodep, ichildp->fromp(), access);
4396 : : return;
4397 : : }
4398 : : }
4399 : : if (AstNodeVarRef* const varrefp = VN_CAST(childp, NodeVarRef)) {
4400 : : varrefp->access(access);
4401 : : } else if (const AstMemberSel* const ichildp = VN_CAST(childp, MemberSel)) {
4402 : : methodCallLValueRecurse(nodep, ichildp->fromp(), access);
4403 : : } else if (const AstStructSel* const ichildp = VN_CAST(childp, StructSel)) {
4404 : : methodCallLValueRecurse(nodep, ichildp->fromp(), access);
4405 : : } else if (const AstNodeSel* const ichildp = VN_CAST(childp, NodeSel)) {
4406 : : methodCallLValueRecurse(nodep, ichildp->fromp(), access);
4407 : : } else if (VN_IS(childp, LambdaArgRef)) {
4408 : : // NOP
4409 : : } else {
4410 : : UINFO(1, " Related node: " << childp);
4411 : : nodep->v3warn(E_UNSUPPORTED, "Unsupported: Non-variable on LHS of built-in method '"
4412 : : << nodep->prettyName() << "'");
4413 : : }
4414 : : }
4415 : : AstCMethodHard* methodCallArray(AstMethodCall* nodep, AstNodeDType* adtypep) {
4416 : : AstCMethodHard* newp = nullptr;
4417 : :
4418 : : if (nodep->name() == "reverse" || nodep->name() == "shuffle" || nodep->name() == "sort"
4419 : : || nodep->name() == "rsort") {
4420 : : AstWith* withp = nullptr;
4421 : : if (nodep->name() == "sort" || nodep->name() == "rsort") {
4422 : : withp = methodWithClause(nodep, false, true, nullptr, nodep->findUInt32DType(),
4423 : : adtypep->subDTypep());
4424 : : }
4425 : : methodOkArguments(nodep, 0, 0);
4426 : : methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::WRITE);
4427 : : newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
4428 : : VCMethod::arrayMethod(nodep->name())};
4429 : : newp->withp(withp);
4430 : : newp->dtypeSetVoid();
4431 : : } else if (nodep->name() == "min" || nodep->name() == "max" || nodep->name() == "unique"
4432 : : || nodep->name() == "unique_index") {
4433 : : AstWith* const withp = methodWithClause(
4434 : : nodep, false, true, nullptr, nodep->findUInt32DType(), adtypep->subDTypep());
4435 : : methodOkArguments(nodep, 0, 0);
4436 : : methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::READ);
4437 : : newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
4438 : : VCMethod::arrayMethod(nodep->name())};
4439 : : newp->withp(withp);
4440 : : if (nodep->name() == "unique_index") {
4441 : : newp->dtypep(newp->findQueueIndexDType());
4442 : : } else {
4443 : : newp->dtypep(queueDTypeIndexedBy(adtypep->subDTypep()));
4444 : : }
4445 : : if (!nodep->firstAbovep()) newp->dtypeSetVoid();
4446 : : } else if (nodep->name() == "find" || nodep->name() == "find_first"
4447 : : || nodep->name() == "find_last" || nodep->name() == "find_index") {
4448 : : AstWith* const withp
4449 : : = methodWithClause(nodep, true, false, nodep->findBitDType(),
4450 : : nodep->findUInt32DType(), adtypep->subDTypep());
4451 : : methodOkArguments(nodep, 0, 0);
4452 : : methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::READ);
4453 : : newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
4454 : : VCMethod::arrayMethod(nodep->name())};
4455 : : newp->withp(withp);
4456 : : newp->dtypep(queueDTypeIndexedBy(adtypep->subDTypep()));
4457 : : if (!nodep->firstAbovep()) newp->dtypeSetVoid();
4458 : : } else if (nodep->name() == "find_index" || nodep->name() == "find_first_index"
4459 : : || nodep->name() == "find_last_index") {
4460 : : AstWith* const withp
4461 : : = methodWithClause(nodep, true, false, nodep->findBitDType(),
4462 : : nodep->findUInt32DType(), adtypep->subDTypep());
4463 : : methodOkArguments(nodep, 0, 0);
4464 : : methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::READ);
4465 : : newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
4466 : : VCMethod::arrayMethod(nodep->name())};
4467 : : newp->withp(withp);
4468 : : newp->dtypep(newp->findQueueIndexDType());
4469 : : if (!nodep->firstAbovep()) newp->dtypeSetVoid();
4470 : : } else if (nodep->name() == "map") {
4471 : : // map() - IEEE 1800-2023 7.12.5
4472 : : // Returns a queue with same element count, each element is the with expression result
4473 : : AstWith* const withp
4474 : : = methodWithClause(nodep, true, false, adtypep->subDTypep(),
4475 : : nodep->findUInt32DType(), adtypep->subDTypep());
4476 : : methodOkArguments(nodep, 0, 0);
4477 : : methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::READ);
4478 : : newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
4479 : : VCMethod::ARRAY_MAP};
4480 : : newp->withp(withp);
4481 : : newp->dtypep(queueDTypeIndexedBy(withp ? withp->dtypep() : adtypep->subDTypep()));
4482 : : if (!nodep->firstAbovep()) newp->dtypeSetVoid();
4483 : : }
4484 : : return newp;
4485 : : }
4486 : : void methodCallDyn(AstMethodCall* nodep, AstDynArrayDType* adtypep) {
4487 : : AstCMethodHard* newp = nullptr;
4488 : : if (nodep->name() == "at") { // Created internally for []
4489 : : methodOkArguments(nodep, 1, 1);
4490 : : iterateCheckSigned32(nodep, "argument", methodArg(nodep, 0), BOTH);
4491 : : newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
4492 : : VCMethod::ARRAY_AT};
4493 : : newp->dtypeFrom(adtypep->subDTypep());
4494 : : } else if (nodep->name() == "atWrite") { // Created internally for []
4495 : : methodOkArguments(nodep, 1, 1);
4496 : : iterateCheckSigned32(nodep, "argument", methodArg(nodep, 0), BOTH);
4497 : : methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::WRITE);
4498 : : newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
4499 : : VCMethod::ARRAY_AT_WRITE};
4500 : : newp->dtypeFrom(adtypep->subDTypep());
4501 : : } else if (nodep->name() == "size") {
4502 : : methodOkArguments(nodep, 0, 0);
4503 : : newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
4504 : : VCMethod::DYN_SIZE};
4505 : : newp->dtypeSetSigned32();
4506 : : } else if (nodep->name() == "delete") { // function void delete()
4507 : : methodOkArguments(nodep, 0, 0);
4508 : : methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::WRITE);
4509 : : newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
4510 : : VCMethod::DYN_CLEAR};
4511 : : newp->dtypeSetVoid();
4512 : : } else if (nodep->name() == "and" || nodep->name() == "or" || nodep->name() == "xor"
4513 : : || nodep->name() == "sum" || nodep->name() == "product") {
4514 : : // All value return
4515 : : AstWith* const withp
4516 : : = methodWithClause(nodep, false, false, adtypep->subDTypep(),
4517 : : nodep->findUInt32DType(), adtypep->subDTypep());
4518 : : methodOkArguments(nodep, 0, 0);
4519 : : methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::READ);
4520 : : newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
4521 : : VCMethod::arrayMethod("r_" + nodep->name())};
4522 : : newp->withp(withp);
4523 : : newp->dtypeFrom(adtypep->subDTypep());
4524 : : if (!nodep->firstAbovep()) newp->dtypeSetVoid();
4525 : : } else if ((newp = methodCallArray(nodep, adtypep))) {
4526 : : } else {
4527 : : nodep->v3warn(E_UNSUPPORTED, "Unsupported/unknown built-in dynamic array method "
4528 : : << nodep->prettyNameQ());
4529 : : nodep->dtypeFrom(adtypep->subDTypep()); // Best guess
4530 : : }
4531 : : if (newp) {
4532 : : newp->protect(false);
4533 : : newp->didWidth(true);
4534 : : nodep->replaceWith(newp);
4535 : : VL_DO_DANGLING(nodep->deleteTree(), nodep);
4536 : : }
4537 : : }
4538 : : void methodCallQueue(AstMethodCall* nodep, AstQueueDType* adtypep) {
4539 : : AstCMethodHard* newp = nullptr;
4540 : : if (nodep->name() == "at" || nodep->name() == "atBack") { // Created internally for []
4541 : : methodOkArguments(nodep, 1, 1);
4542 : : iterateCheckSigned32(nodep, "argument", methodArg(nodep, 0), BOTH);
4543 : : newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
4544 : : VCMethod::arrayMethod(nodep->name())};
4545 : : newp->dtypeFrom(adtypep->subDTypep());
4546 : : } else if (nodep->name() == "atWriteAppend"
4547 : : || nodep->name() == "atWriteAppendBack") { // Created internally for []
4548 : : methodOkArguments(nodep, 1, 1);
4549 : : iterateCheckSigned32(nodep, "argument", methodArg(nodep, 0), BOTH);
4550 : : methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::WRITE);
4551 : : newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
4552 : : VCMethod::arrayMethod(nodep->name())};
4553 : : newp->dtypeFrom(adtypep->subDTypep());
4554 : : } else if (nodep->name() == "num" // function int num()
4555 : : || nodep->name() == "size") {
4556 : : methodOkArguments(nodep, 0, 0);
4557 : : newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
4558 : : VCMethod::DYN_SIZE};
4559 : : newp->dtypeSetSigned32();
4560 : : } else if (nodep->name() == "delete") { // function void delete([input integer index])
4561 : : methodOkArguments(nodep, 0, 1);
4562 : : methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::WRITE);
4563 : : if (!nodep->argsp()) {
4564 : : newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
4565 : : VCMethod::DYN_CLEAR};
4566 : : newp->dtypeSetVoid();
4567 : : } else {
4568 : : iterateCheckSigned32(nodep, "argument", methodArg(nodep, 0), BOTH);
4569 : : AstNodeExpr* const index_exprp = methodCallQueueIndexExpr(nodep);
4570 : : if (index_exprp->isZero()) { // delete(0) is a pop_front
4571 : : newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
4572 : : VCMethod::DYN_POP_FRONT};
4573 : : newp->dtypeFrom(adtypep->subDTypep());
4574 : : newp->dtypeSetVoid();
4575 : : } else {
4576 : : newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
4577 : : VCMethod::DYN_ERASE, index_exprp->unlinkFrBack()};
4578 : : newp->dtypeSetVoid();
4579 : : }
4580 : : }
4581 : : } else if (nodep->name() == "insert") {
4582 : : methodOkArguments(nodep, 2, 2);
4583 : : iterateCheckSigned32(nodep, "index", methodArg(nodep, 0), BOTH);
4584 : : methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::WRITE);
4585 : : AstNodeExpr* const index_exprp = methodCallQueueIndexExpr(nodep);
4586 : : AstArg* const argp = VN_AS(nodep->argsp()->nextp(), Arg);
4587 : : iterateCheckTyped(nodep, "insert value", argp->exprp(), adtypep->subDTypep(), BOTH);
4588 : : if (index_exprp->isZero()) { // insert(0, ...) is a push_front
4589 : : newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
4590 : : VCMethod::DYN_PUSH_FRONT, argp->exprp()->unlinkFrBack()};
4591 : : newp->dtypeSetVoid();
4592 : : } else {
4593 : : newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
4594 : : VCMethod::DYN_INSERT, index_exprp->unlinkFrBack()};
4595 : : newp->addPinsp(argp->exprp()->unlinkFrBack());
4596 : : newp->dtypeSetVoid();
4597 : : }
4598 : : } else if (nodep->name() == "pop_front" || nodep->name() == "pop_back") {
4599 : : methodOkArguments(nodep, 0, 0);
4600 : : // Returns element, so method both consumes (reads) and modifies the queue
4601 : : methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::READWRITE);
4602 : : newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
4603 : : VCMethod::arrayMethod(nodep->name())};
4604 : : newp->dtypeFrom(adtypep->subDTypep());
4605 : : } else if (nodep->name() == "push_back" || nodep->name() == "push_front") {
4606 : : methodOkArguments(nodep, 1, 1);
4607 : : iterateCheckTyped(nodep, "argument", methodArg(nodep, 0), adtypep->subDTypep(), BOTH);
4608 : : methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::WRITE);
4609 : : AstArg* const argp = nodep->argsp();
4610 : : iterateCheckTyped(nodep, "push value", argp->exprp(), adtypep->subDTypep(), BOTH);
4611 : : newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
4612 : : VCMethod::arrayMethod(nodep->name()),
4613 : : argp->exprp()->unlinkFrBack()};
4614 : : newp->dtypeSetVoid();
4615 : : } else if (nodep->name() == "and" || nodep->name() == "or" || nodep->name() == "xor"
4616 : : || nodep->name() == "sum" || nodep->name() == "product") {
4617 : : AstWith* const withp
4618 : : = methodWithClause(nodep, false, false, adtypep->subDTypep(),
4619 : : nodep->findUInt32DType(), adtypep->subDTypep());
4620 : : methodOkArguments(nodep, 0, 0);
4621 : : methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::READ);
4622 : : newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
4623 : : VCMethod::arrayMethod("r_" + nodep->name())};
4624 : : newp->withp(withp);
4625 : : newp->dtypeFrom(withp ? withp->dtypep() : adtypep->subDTypep());
4626 : : if (!nodep->firstAbovep()) newp->dtypeSetVoid();
4627 : : } else if ((newp = methodCallArray(nodep, adtypep))) {
4628 : : } else {
4629 : : nodep->v3warn(E_UNSUPPORTED,
4630 : : "Unsupported/unknown built-in queue method " << nodep->prettyNameQ());
4631 : : nodep->dtypeFrom(adtypep->subDTypep()); // Best guess
4632 : : }
4633 : : if (newp) {
4634 : : newp->protect(false);
4635 : : newp->didWidth(true);
4636 : : nodep->replaceWith(newp);
4637 : : VL_DO_DANGLING(nodep->deleteTree(), nodep);
4638 : : }
4639 : : }
4640 : : AstNodeExpr* methodCallQueueIndexExpr(AstMethodCall* nodep) {
4641 : : AstNode* const index_exprp = nodep->argsp()->exprp();
4642 : : iterateCheckSigned32(nodep, "index", index_exprp, BOTH);
4643 : : VL_DANGLING(index_exprp); // May have been edited
4644 : : return nodep->argsp()->exprp();
4645 : : }
4646 : : void methodCallWarnTiming(AstNodeFTaskRef* const nodep, const std::string& className) {
4647 : : if (v3Global.opt.timing().isSetFalse()) {
4648 : : nodep->v3warn(E_NOTIMING,
4649 : : className << "::" << nodep->name() << "() requires --timing");
4650 : : } else if (!v3Global.opt.timing().isSetTrue()) {
4651 : : nodep->v3warn(E_NEEDTIMINGOPT, "Use --timing or --no-timing to specify how "
4652 : : << className << "::" << nodep->name()
4653 : : << "() should be handled");
4654 : : }
4655 : : }
4656 : : void methodCallIfaceRef(AstMethodCall* nodep, AstIfaceRefDType* adtypep) {
4657 : : AstIface* const ifacep = adtypep->ifacep();
4658 : : UINFO(5, __FUNCTION__ << ":" << nodep);
4659 : : if (AstNodeFTask* const ftaskp
4660 : : = VN_CAST(m_memberMap.findMember(ifacep, nodep->name()), NodeFTask)) {
4661 : : UINFO(5, __FUNCTION__ << "AstNodeFTask" << nodep);
4662 : : if (adtypep->isVirtual()) {
4663 : : for (AstNode* itemp = ifacep->stmtsp(); itemp; itemp = itemp->nextp()) {
4664 : : if (AstVar* const mvarp = VN_CAST(itemp, Var)) { mvarp->sensIfacep(ifacep); }
4665 : : }
4666 : : }
4667 : : userIterate(ftaskp, nullptr);
4668 : : if (ftaskp->isStatic()) {
4669 : : AstArg* const argsp = nodep->argsp();
4670 : : if (argsp) argsp->unlinkFrBackWithNext();
4671 : : AstNodeFTaskRef* newp = nullptr;
4672 : : if (VN_IS(ftaskp, Task)) {
4673 : : newp = new AstTaskRef{nodep->fileline(), VN_AS(ftaskp, Task), argsp};
4674 : : } else {
4675 : : newp = new AstFuncRef{nodep->fileline(), VN_AS(ftaskp, Func), argsp};
4676 : : }
4677 : : newp->classOrPackagep(ifacep);
4678 : : nodep->replaceWith(newp);
4679 : : VL_DO_DANGLING(nodep->deleteTree(), nodep);
4680 : : } else {
4681 : : nodep->taskp(ftaskp);
4682 : : nodep->dtypeFrom(ftaskp);
4683 : : nodep->classOrPackagep(ifacep);
4684 : : if (VN_IS(ftaskp, Task)) nodep->dtypeSetVoid();
4685 : : processFTaskRefArgs(nodep);
4686 : : }
4687 : : return;
4688 : : }
4689 : : nodep->v3error("Member reference from interface to "
4690 : : << nodep->prettyNameQ() << " is not referencing a valid task or function ");
4691 : : }
4692 : : void handleRandomizeArgs(AstNodeFTaskRef* const nodep, AstClass* const classp) {
4693 : : bool hasNonNullArgs = false;
4694 : : AstConst* nullp = nullptr;
4695 : : for (AstArg *argp = nodep->argsp(), *nextp; argp; argp = nextp) {
4696 : : nextp = VN_AS(argp->nextp(), Arg);
4697 : : AstVar* randVarp = nullptr;
4698 : : AstNodeExpr* exprp = argp->exprp();
4699 : : if (AstConst* const constp = VN_CAST(exprp, Const)) {
4700 : : if (constp->num().isNull()) {
4701 : : nullp = constp;
4702 : : continue;
4703 : : }
4704 : : }
4705 : : hasNonNullArgs = true;
4706 : : AstVar* fromVarp = nullptr; // If it's a method call, the leftmost element
4707 : : // of the dot hierarchy
4708 : : if (AstMethodCall* methodCallp = VN_CAST(nodep, MethodCall)) {
4709 : : AstNodeExpr* fromp = methodCallp->fromp();
4710 : : while (AstMemberSel* const memberSelp = VN_CAST(fromp, MemberSel)) {
4711 : : fromp = memberSelp->fromp();
4712 : : }
4713 : : AstVarRef* const varrefp = VN_AS(fromp, VarRef);
4714 : : fromVarp = varrefp->varp();
4715 : : }
4716 : : if (!VN_IS(exprp, VarRef) && !VN_IS(exprp, MemberSel)) {
4717 : : argp->v3error("'randomize()' argument must be a variable contained in "
4718 : : << (fromVarp ? fromVarp->prettyNameQ() : classp->prettyNameQ()));
4719 : : VL_DO_DANGLING(argp->unlinkFrBack()->deleteTree(), argp);
4720 : : continue;
4721 : : }
4722 : : while (exprp) {
4723 : : if (AstMemberSel* const memberSelp = VN_CAST(exprp, MemberSel)) {
4724 : : randVarp = memberSelp->varp();
4725 : : exprp = memberSelp->fromp();
4726 : : } else {
4727 : : if (AstVarRef* const varrefp = VN_CAST(exprp, VarRef)) {
4728 : : randVarp = varrefp->varp();
4729 : : } else {
4730 : : argp->v3warn(
4731 : : E_UNSUPPORTED,
4732 : : "Unsupported: Non-variable expression as 'randomize()' argument");
4733 : : VL_DO_DANGLING(argp->unlinkFrBack()->deleteTree(), argp);
4734 : : }
4735 : : exprp = nullptr;
4736 : : }
4737 : : // All variables in the dot hierarchy must be randomizable
4738 : : if (randVarp && !randVarp->isRand()) randVarp->rand(VRandAttr::RAND_INLINE);
4739 : : }
4740 : : // randVarp is now the leftmost element from the dot hierarchy in argp->exprp()
4741 : : if (randVarp == fromVarp) {
4742 : : // The passed in variable is MemberSel'ected from the MethodCall target
4743 : : } else if (classp->existsMember([&](const AstClass*, const AstVar* memberVarp) {
4744 : : return memberVarp == randVarp;
4745 : : })) {
4746 : : // The passed in variable is contained in the method call target
4747 : : } else {
4748 : : // Passed in a constant or complex expression, or the above conditions are not
4749 : : // met
4750 : : argp->v3error("'randomize()' argument must be a variable contained in "
4751 : : << (fromVarp ? fromVarp->prettyNameQ() : classp->prettyNameQ()));
4752 : : VL_DO_DANGLING(argp->unlinkFrBack()->deleteTree(), argp);
4753 : : }
4754 : : }
4755 : : if (nullp) {
4756 : : if (hasNonNullArgs) {
4757 : : nullp->v3error("Cannot pass more arguments to 'randomize(null)'");
4758 : : } else {
4759 : : nullp->v3warn(E_UNSUPPORTED, "Unsupported: 'randomize(null)'");
4760 : : }
4761 : : }
4762 : : }
4763 : : void methodCallClass(AstMethodCall* nodep, AstClassRefDType* adtypep) {
4764 : : // No need to width-resolve the class, as it was done when we did the child
4765 : : AstClass* const first_classp = adtypep->classp();
4766 : : AstWith* withp = nullptr;
4767 : : if (nodep->name() == "randomize") {
4768 : : VL_RESTORER(m_randomizeFromp);
4769 : : m_randomizeFromp = nodep->fromp();
4770 : : withp = methodWithClause(nodep, false, false, adtypep->findVoidDType(),
4771 : : adtypep->findBitDType(), adtypep);
4772 : : for (AstArg* argp = nodep->argsp(); argp; argp = VN_AS(argp->nextp(), Arg)) {
4773 : : if (argp->exprp()) userIterate(argp->exprp(), WidthVP{SELF, BOTH}.p());
4774 : : }
4775 : : methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::READ);
4776 : : V3Randomize::newRandomizeFunc(m_memberMap, first_classp);
4777 : : handleRandomizeArgs(nodep, first_classp);
4778 : : } else if (nodep->name() == "srandom") {
4779 : : methodOkArguments(nodep, 1, 1);
4780 : : iterateCheckSigned32(nodep, "argument", methodArg(nodep, 0), BOTH);
4781 : : methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::READ);
4782 : : V3Randomize::newSRandomFunc(m_memberMap, first_classp);
4783 : : }
4784 : : UASSERT_OBJ(first_classp, nodep, "Unlinked");
4785 : : for (AstClass* classp = first_classp; classp;) {
4786 : : if (nodep->fileline()->timingOn()) {
4787 : : if (classp->name() == "semaphore" || classp->name() == "process"
4788 : : || VString::startsWith(classp->name(), "mailbox")) {
4789 : : // Find the package the class is in
4790 : : AstPackage* const packagep = getItemPackage(classp);
4791 : : // Check if it's std
4792 : : if (packagep && packagep->name() == "std") {
4793 : : if (classp->name() == "process") {
4794 : : methodCallWarnTiming(nodep, "process");
4795 : : } else if (classp->name() == "semaphore" && nodep->name() == "get") {
4796 : : methodCallWarnTiming(nodep, "semaphore");
4797 : : } else if (nodep->name() == "put" || nodep->name() == "get"
4798 : : || nodep->name() == "peek") {
4799 : : methodCallWarnTiming(nodep, "mailbox");
4800 : : }
4801 : : }
4802 : : }
4803 : : }
4804 : : if (AstNodeFTask* const ftaskp
4805 : : = VN_CAST(m_memberMap.findMember(classp, nodep->name()), NodeFTask)) {
4806 : : userIterate(ftaskp, nullptr);
4807 : : if (ftaskp->isStatic()) {
4808 : : AstArg* const argsp = nodep->argsp();
4809 : : if (argsp) argsp->unlinkFrBackWithNext();
4810 : : AstNodeFTaskRef* newp = nullptr;
4811 : : // We use m_vup to determine task or function, so that later error checks
4812 : : // for funcref->task and taskref->func will pick up properly
4813 : : if (!m_vup) { // Called as task
4814 : : newp = new AstTaskRef{nodep->fileline(), nodep->name(), argsp};
4815 : : } else {
4816 : : newp = new AstFuncRef{nodep->fileline(), nodep->name(), argsp};
4817 : : }
4818 : : newp->taskp(ftaskp); // Not passed above as might be func vs task mismatch
4819 : : newp->classOrPackagep(classp);
4820 : : nodep->replaceWith(newp);
4821 : : VL_DO_DANGLING(nodep->deleteTree(), nodep);
4822 : : } else {
4823 : : nodep->taskp(ftaskp);
4824 : : nodep->dtypeFrom(ftaskp);
4825 : : nodep->classOrPackagep(classp);
4826 : : if (VN_IS(ftaskp, Task)) {
4827 : : if (m_vup && m_vup->prelim() && !VN_IS(nodep->backp(), StmtExpr)) {
4828 : : nodep->v3error(
4829 : : "Cannot call a task/void-function as a member function: "
4830 : : << nodep->prettyNameQ());
4831 : : }
4832 : : nodep->dtypeSetVoid();
4833 : : }
4834 : : if (withp) nodep->withp(withp);
4835 : : processFTaskRefArgs(nodep);
4836 : : }
4837 : : return;
4838 : : } else if (nodep->name() == "get_randstate") {
4839 : : methodOkArguments(nodep, 0, 0);
4840 : : first_classp->baseMostClassp()->needRNG(true);
4841 : : v3Global.useRandomizeMethods(true);
4842 : : AstCMethodHard* const newp
4843 : : = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
4844 : : VCMethod::RNG_GET_RANDSTATE, nullptr};
4845 : : newp->usePtr(true);
4846 : : newp->dtypeSetString();
4847 : : nodep->replaceWith(newp);
4848 : : VL_DO_DANGLING(pushDeletep(nodep), nodep);
4849 : : return;
4850 : : } else if (nodep->name() == "set_randstate") {
4851 : : methodOkArguments(nodep, 1, 1);
4852 : : AstNodeExpr* const expr1p = nodep->argsp()->exprp(); // May edit
4853 : : iterateCheckString(nodep, "LHS", expr1p, BOTH);
4854 : : AstNodeExpr* const exprp = nodep->argsp()->exprp();
4855 : : first_classp->baseMostClassp()->needRNG(true);
4856 : : v3Global.useRandomizeMethods(true);
4857 : : AstCMethodHard* const newp
4858 : : = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
4859 : : VCMethod::RNG_SET_RANDSTATE, exprp->unlinkFrBack()};
4860 : : newp->usePtr(true);
4861 : : newp->dtypeSetVoid();
4862 : : nodep->replaceWith(newp);
4863 : : VL_DO_DANGLING(pushDeletep(nodep), nodep);
4864 : : return;
4865 : : } else if (nodep->name() == "constraint_mode") {
4866 : : v3Global.useRandomizeMethods(true);
4867 : : nodep->dtypep(nodep->findBasicDType(VBasicDTypeKwd::INT));
4868 : : }
4869 : : classp = classp->extendsp() ? classp->extendsp()->classp() : nullptr;
4870 : : }
4871 : : {
4872 : : VSpellCheck speller;
4873 : : for (AstClass* classp = first_classp; classp;) {
4874 : : for (AstNode* itemp = classp->membersp(); itemp; itemp = itemp->nextp()) {
4875 : : if (VN_IS(itemp, NodeFTask)) speller.pushCandidate(itemp->prettyName());
4876 : : }
4877 : : classp = classp->extendsp() ? classp->extendsp()->classp() : nullptr;
4878 : : }
4879 : : const string suggest = speller.bestCandidateMsg(nodep->prettyName());
4880 : : nodep->v3error("Class method "
4881 : : << nodep->prettyNameQ() << " not found in "
4882 : : << first_classp->verilogKwd() << " " << first_classp->prettyNameQ()
4883 : : << "\n"
4884 : : << (suggest.empty() ? "" : nodep->fileline()->warnMore() + suggest));
4885 : : }
4886 : : nodep->dtypeSetSigned32(); // Guess on error
4887 : : }
4888 : : void methodCallConstraint(AstMethodCall* nodep, AstConstraintRefDType*) {
4889 : : if (nodep->name() == "constraint_mode") {
4890 : : // IEEE 1800-2023 18.9
4891 : : methodOkArguments(nodep, 0, 1);
4892 : : if (nodep->argsp()) {
4893 : : iterateCheckBool(nodep, "argument", methodArg(nodep, 0), BOTH);
4894 : : nodep->dtypep(nodep->findBasicDType(VBasicDTypeKwd::INT));
4895 : : } else {
4896 : : nodep->dtypeSetVoid();
4897 : : }
4898 : : v3Global.useRandomizeMethods(true);
4899 : : } else {
4900 : : nodep->v3error("No such constraint method " << nodep->prettyNameQ());
4901 : : nodep->replaceWith(new AstConst{nodep->fileline(), AstConst::BitFalse{}});
4902 : : VL_DO_DANGLING(pushDeletep(nodep), nodep);
4903 : : }
4904 : : }
4905 : : void methodCallRandMode(AstMethodCall* nodep) {
4906 : : methodOkArguments(nodep, 0, 1);
4907 : : // IEEE 1800-2023 18.8
4908 : : if (nodep->argsp()) {
4909 : : iterateCheckBool(nodep, "argument", methodArg(nodep, 0), BOTH);
4910 : : nodep->dtypeSetVoid();
4911 : : } else {
4912 : : nodep->dtypep(nodep->findBasicDType(VBasicDTypeKwd::INT));
4913 : : }
4914 : : v3Global.useRandomizeMethods(true);
4915 : : }
4916 : : void methodCallUnpack(AstMethodCall* nodep, AstUnpackArrayDType* adtypep) {
4917 : : enum : uint8_t {
4918 : : UNKNOWN = 0,
4919 : : ARRAY_OR,
4920 : : ARRAY_AND,
4921 : : ARRAY_XOR,
4922 : : ARRAY_SUM,
4923 : : ARRAY_PRODUCT
4924 : : } methodId;
4925 : :
4926 : : methodId = UNKNOWN;
4927 : : if (nodep->name() == "or") {
4928 : : methodId = ARRAY_OR;
4929 : : } else if (nodep->name() == "and") {
4930 : : methodId = ARRAY_AND;
4931 : : } else if (nodep->name() == "xor") {
4932 : : methodId = ARRAY_XOR;
4933 : : } else if (nodep->name() == "sum") {
4934 : : methodId = ARRAY_SUM;
4935 : : } else if (nodep->name() == "product") {
4936 : : methodId = ARRAY_PRODUCT;
4937 : : }
4938 : :
4939 : : if (methodId) {
4940 : : AstWith* const withp
4941 : : = methodWithClause(nodep, false, false, adtypep->subDTypep(),
4942 : : nodep->findUInt32DType(), adtypep->subDTypep());
4943 : : methodOkArguments(nodep, 0, 0);
4944 : : if (withp) {
4945 : : methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::READ);
4946 : : AstCMethodHard* const newp
4947 : : = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
4948 : : VCMethod::arrayMethod("r_" + nodep->name())};
4949 : : newp->withp(withp);
4950 : : newp->dtypeFrom(withp ? withp->dtypep() : adtypep->subDTypep());
4951 : : if (!nodep->firstAbovep()) newp->dtypeSetVoid();
4952 : : newp->protect(false);
4953 : : newp->didWidth(true);
4954 : : nodep->replaceWith(newp);
4955 : : VL_DO_DANGLING(nodep->deleteTree(), nodep);
4956 : : return;
4957 : : } else {
4958 : : // TODO use this code for small vectors, otherwise use runtime reduction.
4959 : : // Historically we expand all vectors.
4960 : : FileLine* const fl = nodep->fileline();
4961 : : AstNodeExpr* newp = nullptr;
4962 : : for (int i = 0; i < adtypep->elementsConst(); ++i) {
4963 : : AstNodeExpr* const arrayRef = nodep->fromp()->cloneTreePure(false);
4964 : : AstNodeExpr* const selector = new AstArraySel{fl, arrayRef, i};
4965 : : if (!newp) {
4966 : : newp = selector;
4967 : : } else {
4968 : : switch (methodId) {
4969 : : case ARRAY_OR: newp = new AstOr{fl, newp, selector}; break;
4970 : : case ARRAY_AND: newp = new AstAnd{fl, newp, selector}; break;
4971 : : case ARRAY_XOR: newp = new AstXor{fl, newp, selector}; break;
4972 : : case ARRAY_SUM: newp = new AstAdd{fl, newp, selector}; break;
4973 : : case ARRAY_PRODUCT: newp = new AstMul{fl, newp, selector}; break;
4974 : : default: nodep->v3fatalSrc("bad case");
4975 : : }
4976 : : }
4977 : : }
4978 : : nodep->replaceWith(newp);
4979 : : VL_DO_DANGLING(nodep->deleteTree(), nodep);
4980 : : }
4981 : : } else if (AstCMethodHard* newp = methodCallArray(nodep, adtypep)) {
4982 : : newp->protect(false);
4983 : : newp->didWidth(true);
4984 : : nodep->replaceWith(newp);
4985 : : VL_DO_DANGLING(nodep->deleteTree(), nodep);
4986 : : } else {
4987 : : nodep->v3error("Unknown built-in array method " << nodep->prettyNameQ());
4988 : : nodep->dtypeFrom(adtypep->subDTypep()); // Best guess
4989 : : }
4990 : : }
4991 : : void methodCallEvent(AstMethodCall* nodep, AstBasicDType*) {
4992 : : // Method call on event
4993 : : if (nodep->name() == "triggered") {
4994 : : methodOkArguments(nodep, 0, 0);
4995 : : AstCMethodHard* const callp = new AstCMethodHard{
4996 : : nodep->fileline(), nodep->fromp()->unlinkFrBack(), VCMethod::EVENT_IS_TRIGGERED};
4997 : : callp->dtypeSetBit();
4998 : : nodep->replaceWith(callp);
4999 : : VL_DO_DANGLING(pushDeletep(nodep), nodep);
5000 : : } else {
5001 : : nodep->v3error("Unknown built-in event method " << nodep->prettyNameQ());
5002 : : }
5003 : : }
5004 : : void methodCallString(AstMethodCall* nodep, AstBasicDType*) {
5005 : : // Method call on string
5006 : : if (nodep->name() == "len") {
5007 : : // Constant value
5008 : : methodOkArguments(nodep, 0, 0);
5009 : : AstNode* const newp = new AstLenN{nodep->fileline(), nodep->fromp()->unlinkFrBack()};
5010 : : nodep->replaceWith(newp);
5011 : : VL_DO_DANGLING(pushDeletep(nodep), nodep);
5012 : : } else if (nodep->name() == "itoa") {
5013 : : methodOkArguments(nodep, 1, 1);
5014 : : iterateCheckSigned32(nodep, "argument", methodArg(nodep, 0), BOTH);
5015 : : VL_DO_DANGLING(replaceWithSFormat(nodep, "%0d"), nodep);
5016 : : } else if (nodep->name() == "hextoa") {
5017 : : methodOkArguments(nodep, 1, 1);
5018 : : iterateCheckSigned32(nodep, "argument", methodArg(nodep, 0), BOTH);
5019 : : VL_DO_DANGLING(replaceWithSFormat(nodep, "%0x"), nodep);
5020 : : } else if (nodep->name() == "octtoa") {
5021 : : methodOkArguments(nodep, 1, 1);
5022 : : iterateCheckSigned32(nodep, "argument", methodArg(nodep, 0), BOTH);
5023 : : VL_DO_DANGLING(replaceWithSFormat(nodep, "%0o"), nodep);
5024 : : } else if (nodep->name() == "bintoa") {
5025 : : methodOkArguments(nodep, 1, 1);
5026 : : iterateCheckSigned32(nodep, "argument", methodArg(nodep, 0), BOTH);
5027 : : VL_DO_DANGLING(replaceWithSFormat(nodep, "%0b"), nodep);
5028 : : } else if (nodep->name() == "realtoa") {
5029 : : methodOkArguments(nodep, 1, 1);
5030 : : iterateCheckReal(nodep, "argument", methodArg(nodep, 0), BOTH);
5031 : : VL_DO_DANGLING(replaceWithSFormat(nodep, "%g"), nodep);
5032 : : } else if (nodep->name() == "tolower") {
5033 : : methodOkArguments(nodep, 0, 0);
5034 : : AstNode* const newp
5035 : : = new AstToLowerN{nodep->fileline(), nodep->fromp()->unlinkFrBack()};
5036 : : nodep->replaceWith(newp);
5037 : : VL_DO_DANGLING(nodep->deleteTree(), nodep);
5038 : : } else if (nodep->name() == "toupper") {
5039 : : methodOkArguments(nodep, 0, 0);
5040 : : AstNode* const newp
5041 : : = new AstToUpperN{nodep->fileline(), nodep->fromp()->unlinkFrBack()};
5042 : : nodep->replaceWith(newp);
5043 : : VL_DO_DANGLING(nodep->deleteTree(), nodep);
5044 : : } else if (nodep->name() == "compare" || nodep->name() == "icompare") {
5045 : : const bool ignoreCase = nodep->name()[0] == 'i';
5046 : : methodOkArguments(nodep, 1, 1);
5047 : : iterateCheckString(nodep, "argument", methodArg(nodep, 0), BOTH);
5048 : : AstArg* const argp = nodep->argsp();
5049 : : AstNodeExpr* const lhs = nodep->fromp()->unlinkFrBack();
5050 : : AstNodeExpr* const rhs = argp->exprp()->unlinkFrBack();
5051 : : AstNode* const newp = new AstCompareNN{nodep->fileline(), lhs, rhs, ignoreCase};
5052 : : nodep->replaceWith(newp);
5053 : : VL_DO_DANGLING(nodep->deleteTree(), nodep);
5054 : : } else if (nodep->name() == "putc") {
5055 : : methodOkArguments(nodep, 2, 2);
5056 : : iterateCheckSigned32(nodep, "argument 0", methodArg(nodep, 0), BOTH);
5057 : : iterateCheckSigned8(nodep, "argument 1", methodArg(nodep, 1), BOTH);
5058 : : AstArg* const arg0p = nodep->argsp();
5059 : : AstArg* const arg1p = VN_AS(arg0p->nextp(), Arg);
5060 : : AstNodeVarRef* const fromp = VN_AS(nodep->fromp()->unlinkFrBack(), VarRef);
5061 : : AstNodeExpr* const rhsp = arg0p->exprp()->unlinkFrBack();
5062 : : AstNodeExpr* const thsp = arg1p->exprp()->unlinkFrBack();
5063 : : AstVarRef* const varrefp
5064 : : = new AstVarRef{nodep->fileline(), fromp->varp(), VAccess::READ};
5065 : : AstNode* const newp = new AstAssign{
5066 : : nodep->fileline(), fromp, new AstPutcN{nodep->fileline(), varrefp, rhsp, thsp}};
5067 : : fromp->access(VAccess::WRITE);
5068 : : pushDeletep(nodep->backp());
5069 : : VL_DO_DANGLING(nodep->backp()->replaceWith(newp), nodep);
5070 : : } else if (nodep->name() == "getc") {
5071 : : methodOkArguments(nodep, 1, 1);
5072 : : iterateCheckSigned32(nodep, "argument", methodArg(nodep, 0), BOTH);
5073 : : AstArg* const arg0p = nodep->argsp();
5074 : : AstNodeExpr* const lhsp = nodep->fromp()->unlinkFrBack();
5075 : : AstNodeExpr* const rhsp = arg0p->exprp()->unlinkFrBack();
5076 : : AstNodeExpr* const newp = new AstGetcN{nodep->fileline(), lhsp, rhsp};
5077 : : nodep->replaceWith(newp);
5078 : : VL_DO_DANGLING(nodep->deleteTree(), nodep);
5079 : : } else if (nodep->name() == "substr") {
5080 : : methodOkArguments(nodep, 2, 2);
5081 : : iterateCheckSigned32(nodep, "argument 0", methodArg(nodep, 0), BOTH);
5082 : : iterateCheckSigned32(nodep, "argument 1", methodArg(nodep, 1), BOTH);
5083 : : AstArg* const arg0p = nodep->argsp();
5084 : : AstArg* const arg1p = VN_AS(arg0p->nextp(), Arg);
5085 : : AstNodeExpr* const lhsp = nodep->fromp()->unlinkFrBack();
5086 : : AstNodeExpr* const rhsp = arg0p->exprp()->unlinkFrBack();
5087 : : AstNodeExpr* const thsp = arg1p->exprp()->unlinkFrBack();
5088 : : AstNodeExpr* const newp = new AstSubstrN{nodep->fileline(), lhsp, rhsp, thsp};
5089 : : nodep->replaceWith(newp);
5090 : : VL_DO_DANGLING(nodep->deleteTree(), nodep);
5091 : : } else if (nodep->name() == "atobin" || nodep->name() == "atohex"
5092 : : || nodep->name() == "atoi" || nodep->name() == "atooct"
5093 : : || nodep->name() == "atoreal") {
5094 : : AstAtoN::FmtType fmt;
5095 : : if (nodep->name() == "atobin") {
5096 : : fmt = AstAtoN::ATOBIN;
5097 : : } else if (nodep->name() == "atohex") {
5098 : : fmt = AstAtoN::ATOHEX;
5099 : : } else if (nodep->name() == "atoi") {
5100 : : fmt = AstAtoN::ATOI;
5101 : : } else if (nodep->name() == "atooct") {
5102 : : fmt = AstAtoN::ATOOCT;
5103 : : } else if (nodep->name() == "atoreal") {
5104 : : fmt = AstAtoN::ATOREAL;
5105 : : } else {
5106 : : nodep->v3fatalSrc("Bad case");
5107 : : fmt = AstAtoN::ATOI;
5108 : : } // dummy assignment to suppress compiler warning
5109 : : methodOkArguments(nodep, 0, 0);
5110 : : AstNode* const newp
5111 : : = new AstAtoN{nodep->fileline(), nodep->fromp()->unlinkFrBack(), fmt};
5112 : : nodep->replaceWith(newp);
5113 : : VL_DO_DANGLING(nodep->deleteTree(), nodep);
5114 : : } else {
5115 : : nodep->v3error("Unknown built-in string method " << nodep->prettyNameQ());
5116 : : }
5117 : : }
5118 : : AstQueueDType* queueDTypeIndexedBy(AstNodeDType* indexDTypep) {
5119 : : // Return a Queue data type with the specified index, remembering so can use again if
5120 : : // needed
5121 : : if (AstQueueDType* const queuep = m_queueDTypeIndexed[indexDTypep]) {
5122 : : return queuep;
5123 : : } else {
5124 : : AstQueueDType* const newp
5125 : : = new AstQueueDType{indexDTypep->fileline(), indexDTypep, nullptr};
5126 : : v3Global.rootp()->typeTablep()->addTypesp(newp);
5127 : : m_queueDTypeIndexed[indexDTypep] = newp;
5128 : : return newp;
5129 : : }
5130 : : }
5131 : :
5132 : : void visit(AstNew* nodep) override {
5133 : : if (nodep->didWidth()) return;
5134 : : AstClass* classp = nullptr;
5135 : : bool assign = false;
5136 : : if (VN_IS(nodep->backp(), Assign)) { // assignment case
5137 : : assign = true;
5138 : : AstNode* warnp = nullptr;
5139 : : AstClassRefDType* refp = nullptr;
5140 : :
5141 : : if (nodep->isScoped()) { // = ClassOrPackage::new
5142 : : UASSERT_OBJ(nodep->classOrPackagep(), nodep, "Unlinked classOrPackage");
5143 : : warnp = nodep->classOrPackagep();
5144 : : if (AstClass* const clsp = VN_CAST(warnp, Class)) {
5145 : : AstClassRefDType* const adtypep
5146 : : = new AstClassRefDType{nodep->fileline(), clsp, nullptr};
5147 : : v3Global.rootp()->typeTablep()->addTypesp(adtypep);
5148 : : refp = adtypep;
5149 : : }
5150 : : } else { // = new
5151 : : warnp = m_vup->dtypeNullp();
5152 : : refp = m_vup ? VN_CAST(m_vup->dtypeNullSkipRefp(), ClassRefDType) : nullptr;
5153 : : }
5154 : : if (!refp) { // e.g. int a = new;
5155 : : nodep->v3error(
5156 : : "new() assignment not legal to non-class "
5157 : : + (VN_IS(warnp, NodeDType)
5158 : : ? ("data type "s + VN_AS(warnp, NodeDType)->prettyDTypeNameQ())
5159 : : : warnp ? warnp->prettyNameQ()
5160 : : : ""));
5161 : : nodep->dtypep(m_vup->dtypep());
5162 : : return;
5163 : : }
5164 : : nodep->dtypep(refp);
5165 : :
5166 : : classp = refp->classp();
5167 : : UASSERT_OBJ(classp, nodep, "Unlinked");
5168 : : if (AstNodeFTask* const ftaskp
5169 : : = VN_CAST(m_memberMap.findMember(classp, "new"), Func)) {
5170 : : nodep->taskp(ftaskp);
5171 : : nodep->classOrPackagep(classp);
5172 : : } else {
5173 : : // Either made explicitly or V3LinkDot made implicitly
5174 : : classp->v3fatalSrc("Can't find class's new");
5175 : : }
5176 : : } else { // super.new case
5177 : : // in this case class and taskp() should be properly linked in V3LinkDot.cpp during
5178 : : // "super" reference resolution
5179 : : classp = VN_CAST(nodep->classOrPackagep(), Class);
5180 : : UASSERT_OBJ(classp, nodep, "Unlinked classOrPackagep()");
5181 : : UASSERT_OBJ(nodep->taskp(), nodep, "Unlinked taskp()");
5182 : : }
5183 : : processFTaskRefArgs(nodep);
5184 : : if (!assign) nodep->dtypeFrom(nodep->taskp());
5185 : : }
5186 : : void visit(AstNewCopy* nodep) override {
5187 : : if (nodep->didWidthAndSet()) return;
5188 : : AstClassRefDType* const refp = VN_CAST(m_vup->dtypeNullSkipRefp(), ClassRefDType);
5189 : : if (!refp) { // e.g. int a = new;
5190 : : nodep->v3error("new() cannot copy from non-class data type "
5191 : : + (m_vup->dtypeNullp() ? m_vup->dtypep()->prettyDTypeNameQ() : ""));
5192 : : nodep->dtypep(m_vup->dtypep());
5193 : : return;
5194 : : }
5195 : : nodep->dtypep(refp);
5196 : : userIterateChildren(nodep, WidthVP{SELF, BOTH}.p());
5197 : : if (!similarDTypeRecurse(nodep->dtypep(), nodep->rhsp()->dtypep())) {
5198 : : nodep->rhsp()->v3error("New-as-copier passed different data type '"
5199 : : << nodep->dtypep()->prettyTypeName() << "' than expected '"
5200 : : << nodep->rhsp()->dtypep()->prettyTypeName() << "'");
5201 : : }
5202 : : }
5203 : : void visit(AstNewDynamic* nodep) override {
5204 : : if (nodep->didWidthAndSet()) return;
5205 : : AstDynArrayDType* const adtypep = VN_CAST(m_vup->dtypeNullSkipRefp(), DynArrayDType);
5206 : : if (!adtypep) { // e.g. int a = new;
5207 : : nodep->v3error(
5208 : : "dynamic new() not expected in this context (data type must be dynamic array)");
5209 : : return;
5210 : : }
5211 : : // The AstNodeAssign visitor will be soon be replacing this node, make sure it gets it
5212 : : if (!VN_IS(nodep->backp(), NodeAssign)) {
5213 : : UINFO(1, "Got backp " << nodep->backp());
5214 : : nodep->v3error(
5215 : : "dynamic new() not expected in this context (expected under an assign)");
5216 : : return;
5217 : : }
5218 : : nodep->dtypep(adtypep);
5219 : : if (m_vup && m_vup->prelim()) {
5220 : : iterateCheckSigned32(nodep, "new() size", nodep->sizep(), BOTH);
5221 : : }
5222 : : if (nodep->rhsp()) {
5223 : : iterateCheckTyped(nodep, "Dynamic array new RHS", nodep->rhsp(), adtypep, BOTH);
5224 : : }
5225 : : }
5226 : :
5227 : : void visit(AstTaggedExpr* nodep) override {
5228 : : // Tagged union expressions are currently unsupported
5229 : : nodep->v3warn(E_UNSUPPORTED, "Unsupported: tagged union");
5230 : : // Set a placeholder type to allow further processing
5231 : : nodep->dtypeSetBit();
5232 : : userIterateChildren(nodep, m_vup);
5233 : : }
5234 : : void visit(AstTaggedPattern* nodep) override {
5235 : : // Tagged patterns are currently unsupported
5236 : : nodep->v3warn(E_UNSUPPORTED, "Unsupported: tagged pattern");
5237 : : nodep->dtypeSetBit();
5238 : : userIterateChildren(nodep, m_vup);
5239 : : }
5240 : : void visit(AstPatternVar* nodep) override {
5241 : : // Pattern variable bindings are currently unsupported
5242 : : nodep->v3warn(E_UNSUPPORTED, "Unsupported: pattern variable");
5243 : : nodep->dtypeSetBit();
5244 : : }
5245 : : void visit(AstPatternStar* nodep) override {
5246 : : // Pattern wildcards are currently unsupported
5247 : : nodep->v3warn(E_UNSUPPORTED, "Unsupported: pattern wildcard");
5248 : : nodep->dtypeSetBit();
5249 : : }
5250 : : void visit(AstMatches* nodep) override {
5251 : : // Matches operator is currently unsupported
5252 : : nodep->v3warn(E_UNSUPPORTED, "Unsupported: matches operator");
5253 : : // Matches returns a boolean
5254 : : nodep->dtypeSetBit();
5255 : : userIterateChildren(nodep, m_vup);
5256 : : }
5257 : :
5258 : : void visit(AstPattern* nodep) override {
5259 : : if (nodep->didWidthAndSet()) return;
5260 : : UINFO(9, "PATTERN " << nodep);
5261 : : if (nodep->childDTypep()) { // data_type '{ pattern }
5262 : : nodep->dtypep(iterateEditMoveDTypep(nodep, nodep->subDTypep()));
5263 : : }
5264 : : if (!nodep->dtypep() && m_vup->dtypeNullp()) { // Get it from parent assignment/pin/etc
5265 : : nodep->dtypep(m_vup->dtypep());
5266 : : }
5267 : : AstNodeDType* dtypep = nodep->dtypep();
5268 : : if (!dtypep) {
5269 : : nodep->v3warn(E_UNSUPPORTED, "Unsupported/Illegal: Assignment pattern"
5270 : : " member not underneath a supported construct: "
5271 : : << nodep->backp()->prettyTypeName());
5272 : :
5273 : : if (nodep->backp() && (VN_IS(nodep->backp(), Eq) || VN_IS(nodep->backp(), Neq)))
5274 : : return;
5275 : : nodep->replaceWith(new AstConst{nodep->fileline(), AstConst::BitFalse{}});
5276 : : VL_DO_DANGLING(pushDeletep(nodep), nodep);
5277 : : return;
5278 : : }
5279 : : {
5280 : : dtypep = dtypep->skipRefp();
5281 : : nodep->dtypep(dtypep);
5282 : : UINFO(9, " dtypep " << dtypep);
5283 : : nodep->dtypep(dtypep);
5284 : : // Determine replication count, and replicate initial value as
5285 : : // widths need to be individually determined
5286 : : for (AstPatMember* patp = VN_AS(nodep->itemsp(), PatMember); patp;
5287 : : patp = VN_AS(patp->nextp(), PatMember)) {
5288 : : const int times = visitPatMemberRep(patp);
5289 : : for (int i = 1; i < times; i++) {
5290 : : AstPatMember* const newp = patp->cloneTree(false);
5291 : : patp->addNextHere(newp);
5292 : : // This loop will see the new elements as part of nextp()
5293 : : }
5294 : : }
5295 : : // Convert any PatMember with multiple items to multiple PatMembers
5296 : : for (AstPatMember* patp = VN_AS(nodep->itemsp(), PatMember); patp;
5297 : : patp = VN_AS(patp->nextp(), PatMember)) {
5298 : : if (patp->lhssp()->nextp()) {
5299 : : // Can't just addNext, as would add to end of all members.
5300 : : // So detach, add next and reattach
5301 : : VNRelinker relinkHandle;
5302 : : patp->unlinkFrBack(&relinkHandle);
5303 : : while (AstNodeExpr* const movep = VN_AS(patp->lhssp()->nextp(), NodeExpr)) {
5304 : : movep->unlinkFrBack(); // Not unlinkFrBackWithNext, just one
5305 : : AstNode* newkeyp = nullptr;
5306 : : if (patp->keyp()) newkeyp = patp->keyp()->cloneTree(true);
5307 : : AstPatMember* const newp
5308 : : = new AstPatMember{patp->fileline(), movep, newkeyp, nullptr};
5309 : : patp->addNext(newp);
5310 : : }
5311 : : relinkHandle.relink(patp);
5312 : : }
5313 : : }
5314 : : AstPatMember* defaultp = nullptr;
5315 : : for (AstPatMember* patp = VN_AS(nodep->itemsp(), PatMember); patp;) {
5316 : : AstPatMember* const nextp = VN_AS(patp->nextp(), PatMember);
5317 : : if (patp->isDefault()) {
5318 : : if (defaultp) {
5319 : : VL_DO_DANGLING(pushDeletep(defaultp), defaultp);
5320 : : nodep->v3error("Multiple '{ default: } clauses");
5321 : : }
5322 : : defaultp = patp;
5323 : : patp->unlinkFrBack();
5324 : : }
5325 : : patp = nextp;
5326 : : }
5327 : : while (const AstConstDType* const vdtypep = VN_CAST(dtypep, ConstDType)) {
5328 : : dtypep = vdtypep->subDTypep()->skipRefp();
5329 : : }
5330 : :
5331 : : userIterate(dtypep, WidthVP{SELF, BOTH}.p());
5332 : :
5333 : : if (auto* const vdtypep = VN_CAST(dtypep, NodeUOrStructDType)) {
5334 : : patternUOrStruct(nodep, vdtypep, defaultp);
5335 : : } else if (auto* const vdtypep = VN_CAST(dtypep, NodeArrayDType)) {
5336 : : patternArray(nodep, vdtypep, defaultp);
5337 : : } else if (auto* const vdtypep = VN_CAST(dtypep, AssocArrayDType)) {
5338 : : patternAssoc(nodep, vdtypep, defaultp);
5339 : : } else if (auto* const vdtypep = VN_CAST(dtypep, WildcardArrayDType)) {
5340 : : patternWildcard(nodep, vdtypep, defaultp);
5341 : : } else if (auto* const vdtypep = VN_CAST(dtypep, DynArrayDType)) {
5342 : : patternDynArray(nodep, vdtypep, defaultp);
5343 : : } else if (auto* const vdtypep = VN_CAST(dtypep, QueueDType)) {
5344 : : patternQueue(nodep, vdtypep, defaultp);
5345 : : } else if (VN_IS(dtypep, BasicDType) && VN_AS(dtypep, BasicDType)->isRanged()) {
5346 : : patternBasic(nodep, dtypep, defaultp);
5347 : : } else {
5348 : : nodep->v3warn(
5349 : : E_UNSUPPORTED,
5350 : : "Unsupported: Assignment pattern applies against non-struct/union data type: "
5351 : : << dtypep->prettyDTypeNameQ());
5352 : : nodep->replaceWith(new AstConst{nodep->fileline(), AstConst::BitFalse{}});
5353 : : }
5354 : :
5355 : : // Done with the Pattern
5356 : : VL_DO_DANGLING(pushDeletep(nodep), nodep);
5357 : : if (defaultp) VL_DO_DANGLING(pushDeletep(defaultp), defaultp);
5358 : : }
5359 : : }
5360 : : void patternUOrStruct(AstPattern* nodep, AstNodeUOrStructDType* vdtypep,
5361 : : AstPatMember* defaultp) {
5362 : : // Due to "default" and tagged patterns, we need to determine
5363 : : // which member each AstPatMember corresponds to before we can
5364 : : // determine the dtypep for that PatMember's value, and then
5365 : : // width the initial value appropriately.
5366 : : using PatMap = std::map<const AstMemberDType*, AstPatMember*>;
5367 : : PatMap patmap; // Store member: value
5368 : : DTypeMap dtypemap; // Store data_type: default_value
5369 : : {
5370 : : const AstMemberDType* memp = vdtypep->membersp();
5371 : : AstPatMember* patp = VN_CAST(nodep->itemsp(), PatMember);
5372 : : while (patp) {
5373 : : do {
5374 : : if (patp->keyp()) {
5375 : : // '{member:value} or '{data_type: default_value}
5376 : : if (const AstText* textp = VN_CAST(patp->keyp(), Text)) {
5377 : : // member: value
5378 : : memp = VN_CAST(m_memberMap.findMember(vdtypep, textp->text()),
5379 : : MemberDType);
5380 : : if (!memp) {
5381 : : patp->keyp()->v3error("Assignment pattern key '"
5382 : : << textp->text() << "' not found as member");
5383 : : break;
5384 : : } else {
5385 : : const std::pair<PatMap::iterator, bool> ret
5386 : : = patmap.emplace(memp, patp);
5387 : : if (!ret.second) {
5388 : : patp->v3error("Assignment pattern contains duplicate entry: "
5389 : : << VN_AS(patp->keyp(), Text)->text());
5390 : : }
5391 : : memp = VN_AS(memp->nextp(), MemberDType);
5392 : : }
5393 : : } else if (AstNodeDType* nodedtypep = VN_CAST(patp->keyp(), NodeDType)) {
5394 : : // data_type: default_value
5395 : : userIterate(nodedtypep, WidthVP{SELF, BOTH}.p());
5396 : : const string dtype = nodedtypep->dtypep()->prettyDTypeName(true);
5397 : : // Override stored default_value
5398 : : dtypemap[dtype] = patp;
5399 : : } else {
5400 : : // Undefined pattern
5401 : : patp->keyp()->v3error(
5402 : : "Assignment pattern key not supported/understood: "
5403 : : << patp->keyp()->prettyTypeName());
5404 : : }
5405 : : } else if (memp) {
5406 : : // constant expr
5407 : : const std::pair<PatMap::iterator, bool> ret = patmap.emplace(memp, patp);
5408 : : if (!ret.second) {
5409 : : patp->v3error("Assignment pattern contains duplicate entry: "
5410 : : << VN_AS(patp->keyp(), Text)->text());
5411 : : }
5412 : : memp = VN_AS(memp->nextp(), MemberDType);
5413 : : } else {
5414 : : patp->v3error(
5415 : : "Assignment pattern contains more entries than structure members");
5416 : : }
5417 : : } while (false);
5418 : :
5419 : : // Next
5420 : : if (patp) patp = VN_AS(patp->nextp(), PatMember);
5421 : : }
5422 : : }
5423 : : AstNodeExpr* newp = nullptr;
5424 : : if (vdtypep->packed()) {
5425 : : for (AstMemberDType* memp = vdtypep->membersp(); memp;
5426 : : memp = VN_AS(memp->nextp(), MemberDType)) {
5427 : : const auto it = patmap.find(memp);
5428 : : if (it == patmap.end()) { // default or default_type assignment
5429 : : if (AstNodeUOrStructDType* const memp_nested_vdtypep
5430 : : = VN_CAST(memp->virtRefDTypep(), NodeUOrStructDType)) {
5431 : : newp = nestedvalueConcat_patternUOrStruct(memp_nested_vdtypep, defaultp,
5432 : : newp, nodep, dtypemap);
5433 : : } else {
5434 : : AstPatMember* const patp = defaultPatp_patternUOrStruct(
5435 : : nodep, memp, vdtypep, defaultp, dtypemap);
5436 : : newp = valueConcat_patternUOrStruct(patp, newp, memp, nodep);
5437 : : if (patp) VL_DO_DANGLING(pushDeletep(patp), patp);
5438 : : }
5439 : : } else { // member assignment
5440 : : newp = valueConcat_patternUOrStruct(it->second, newp, memp, nodep);
5441 : : }
5442 : : }
5443 : : } else { // Unpacked
5444 : : AstConsPackMember* membersp = nullptr;
5445 : : for (AstMemberDType* memp = vdtypep->membersp(); memp;
5446 : : memp = VN_AS(memp->nextp(), MemberDType)) {
5447 : : const auto it = patmap.find(memp);
5448 : : AstPatMember* patp = nullptr;
5449 : : if (it == patmap.end()) { // Default or default_type assignment
5450 : : patp = defaultPatp_patternUOrStruct(nodep, memp, vdtypep, defaultp, dtypemap);
5451 : : pushDeletep(patp);
5452 : : } else {
5453 : : patp = it->second; // Member assignment
5454 : : }
5455 : : patp->dtypep(memp);
5456 : : AstNodeExpr* const valuep = patternMemberValueIterate(patp);
5457 : : AstConsPackMember* const cpmp
5458 : : = new AstConsPackMember{patp->fileline(), memp, valuep};
5459 : : membersp = AstNode::addNextNull(membersp, cpmp);
5460 : : }
5461 : : newp = new AstConsPackUOrStruct{nodep->fileline(), vdtypep, membersp};
5462 : : }
5463 : : if (newp) {
5464 : : nodep->replaceWith(newp);
5465 : : } else {
5466 : : nodep->v3error("Assignment pattern with no members");
5467 : : }
5468 : : }
5469 : :
5470 : : AstNodeExpr* nestedvalueConcat_patternUOrStruct(AstNodeUOrStructDType* memp_vdtypep,
5471 : : AstPatMember* defaultp, AstNodeExpr* newp,
5472 : : AstPattern* nodep, const DTypeMap& dtypemap) {
5473 : : for (AstMemberDType* memp_nested = memp_vdtypep->membersp(); memp_nested;
5474 : : memp_nested = VN_AS(memp_nested->nextp(), MemberDType)) {
5475 : : if (AstNodeUOrStructDType* const memp_multinested_vdtypep
5476 : : = VN_CAST(memp_nested->virtRefDTypep(), NodeUOrStructDType)) {
5477 : : // When unpacked struct/union is supported this if will need some additional
5478 : : // conditions
5479 : : newp = nestedvalueConcat_patternUOrStruct(memp_multinested_vdtypep, defaultp, newp,
5480 : : nodep, dtypemap);
5481 : : } else {
5482 : : AstPatMember* const patp = defaultPatp_patternUOrStruct(
5483 : : nodep, memp_nested, memp_vdtypep, defaultp, dtypemap);
5484 : : newp = valueConcat_patternUOrStruct(patp, newp, memp_nested, nodep);
5485 : : if (patp) VL_DO_DANGLING(pushDeletep(patp), patp);
5486 : : }
5487 : : }
5488 : : return newp;
5489 : : }
5490 : :
5491 : : AstPatMember* defaultPatp_patternUOrStruct(AstPattern* nodep, AstMemberDType* memp,
5492 : : AstNodeUOrStructDType* memp_vdtypep,
5493 : : AstPatMember* defaultp, const DTypeMap& dtypemap) {
5494 : : const string memp_DType = memp->virtRefDTypep()->prettyDTypeName(true);
5495 : : const auto it = dtypemap.find(memp_DType);
5496 : : if (it != dtypemap.end()) {
5497 : : // default_value for data_type
5498 : : return it->second->cloneTree(false);
5499 : : }
5500 : : if (defaultp) {
5501 : : // default_value for any unmatched member yet
5502 : : return defaultp->cloneTree(false);
5503 : : }
5504 : : if (memp->valuep()) {
5505 : : return new AstPatMember{nodep->fileline(),
5506 : : VN_AS(memp->valuep()->cloneTree(false), NodeExpr),
5507 : : new AstText{nodep->fileline(), memp->name()}, nullptr};
5508 : : }
5509 : : if (!VN_IS(memp_vdtypep, UnionDType)) {
5510 : : nodep->v3error("Assignment pattern missed initializing elements: "
5511 : : << memp->virtRefDTypep()->prettyDTypeNameQ() << " "
5512 : : << memp->prettyNameQ());
5513 : : }
5514 : : return nullptr;
5515 : : }
5516 : :
5517 : : AstNodeExpr* valueConcat_patternUOrStruct(AstPatMember* patp, AstNodeExpr* newp,
5518 : : AstMemberDType* memp, AstPattern* nodep) {
5519 : : if (patp) {
5520 : : patp->dtypep(memp);
5521 : : AstNodeExpr* const valuep = patternMemberValueIterate(patp);
5522 : : if (!newp) {
5523 : : newp = valuep;
5524 : : } else {
5525 : : AstConcat* const concatp = new AstConcat{patp->fileline(), newp, valuep};
5526 : : // Have resolved widths, no reason to warn about widths on the concat itself
5527 : : newp->fileline()->modifyWarnOff(V3ErrorCode::WIDTHCONCAT, true);
5528 : : valuep->fileline()->modifyWarnOff(V3ErrorCode::WIDTHCONCAT, true);
5529 : : newp = concatp;
5530 : : newp->dtypeSetLogicSized(concatp->lhsp()->width() + concatp->rhsp()->width(),
5531 : : nodep->dtypep()->numeric());
5532 : : }
5533 : : }
5534 : : return newp;
5535 : : }
5536 : :
5537 : : AstPatMember* defaultPatp_patternArray(AstPatMember* defaultp, AstNodeDType* elemDTypep) {
5538 : : AstNodeExpr* const valuep = defaultp->lhssp()->cloneTree(false);
5539 : : AstNodeDType* const elemDTypeSkipRefp = elemDTypep->skipRefp();
5540 : :
5541 : : if (!VN_IS(elemDTypeSkipRefp, UnpackArrayDType)) {
5542 : : VL_DO_DANGLING(pushDeletep(valuep), valuep);
5543 : : return defaultp->cloneTree(false);
5544 : : }
5545 : : if (VN_IS(valuep, Pattern)) {
5546 : : VL_DO_DANGLING(pushDeletep(valuep), valuep);
5547 : : return defaultp->cloneTree(false);
5548 : : }
5549 : : if (!valuep->dtypep()) userIterate(valuep, WidthVP{SELF, BOTH}.p());
5550 : : if (valuep->dtypep()
5551 : : && AstNode::computeCastable(valuep->dtypep()->skipRefp(), elemDTypeSkipRefp, nullptr)
5552 : : .isAssignable()) {
5553 : : VL_DO_DANGLING(pushDeletep(valuep), valuep);
5554 : : return defaultp->cloneTree(false);
5555 : : }
5556 : :
5557 : : AstPatMember* const nestedDefaultp
5558 : : = new AstPatMember{defaultp->fileline(), valuep, nullptr, nullptr};
5559 : : nestedDefaultp->isDefault(true);
5560 : : AstPattern* const recursivePatternp = new AstPattern{defaultp->fileline(), nestedDefaultp};
5561 : : return new AstPatMember{defaultp->fileline(), recursivePatternp, nullptr, nullptr};
5562 : : }
5563 : :
5564 : : void patternArray(AstPattern* nodep, AstNodeArrayDType* arrayDtp, AstPatMember* defaultp) {
5565 : : const VNumRange range = arrayDtp->declRange();
5566 : : PatVecMap patmap = patVectorMap(nodep, range);
5567 : : UINFO(9, "ent " << range.left() << " to " << range.right());
5568 : : AstNode* newp = nullptr;
5569 : : bool allConstant = true;
5570 : : const bool isConcat = nodep->itemsp() && VN_AS(nodep->itemsp(), PatMember)->isConcat();
5571 : : for (int entn = 0, ent = range.left(); entn < range.elements();
5572 : : ++entn, ent += range.leftToRightInc()) {
5573 : : AstPatMember* newpatp = nullptr;
5574 : : AstPatMember* patp = nullptr;
5575 : : const auto it = patmap.find(ent);
5576 : : if (it == patmap.end()) {
5577 : : if (defaultp) {
5578 : : newpatp = defaultPatp_patternArray(defaultp, arrayDtp->subDTypep());
5579 : : patp = newpatp;
5580 : : } else if (!(VN_IS(arrayDtp, UnpackArrayDType) && !allConstant && isConcat)) {
5581 : : // If arrayDtp is an unpacked array and item is not constant,
5582 : : // the number of elements cannot be determined here as the dtype of each
5583 : : // element is not set yet. V3Slice checks for such cases.
5584 : : nodep->v3error("Assignment pattern missed initializing elements: " << ent);
5585 : : }
5586 : : } else {
5587 : : patp = it->second;
5588 : : patmap.erase(it);
5589 : : }
5590 : :
5591 : : if (patp) {
5592 : : // Don't want the RHS an array
5593 : : allConstant &= VN_IS(patp->lhssp(), Const);
5594 : : patp->dtypep(arrayDtp->subDTypep());
5595 : : AstNodeExpr* const valuep = patternMemberValueIterate(patp);
5596 : : if (VN_IS(arrayDtp, UnpackArrayDType)) {
5597 : : if (!newp) {
5598 : : AstInitArray* const newap
5599 : : = new AstInitArray{nodep->fileline(), arrayDtp, nullptr};
5600 : : newp = newap;
5601 : : }
5602 : : VN_AS(newp, InitArray)->addIndexValuep(ent - range.lo(), valuep);
5603 : : } else { // Packed. Convert to concat for now.
5604 : : if (!newp) {
5605 : : newp = valuep;
5606 : : } else {
5607 : : AstConcat* const concatp
5608 : : = new AstConcat{patp->fileline(), VN_AS(newp, NodeExpr), valuep};
5609 : : newp = concatp;
5610 : : newp->dtypeSetLogicSized(concatp->lhsp()->width()
5611 : : + concatp->rhsp()->width(),
5612 : : nodep->dtypep()->numeric());
5613 : : }
5614 : : }
5615 : : }
5616 : : if (newpatp) VL_DO_DANGLING(pushDeletep(newpatp), newpatp);
5617 : : }
5618 : : if (!patmap.empty()) nodep->v3error("Assignment pattern with too many elements");
5619 : : if (newp) {
5620 : : nodep->replaceWith(newp);
5621 : : } else {
5622 : : nodep->v3error("Assignment pattern with no members");
5623 : : }
5624 : : // UINFOTREE(9, newp, "", "apat-out");
5625 : : }
5626 : : void patternAssoc(AstPattern* nodep, AstAssocArrayDType* arrayDtp, AstPatMember* defaultp) {
5627 : : AstNodeExpr* defaultValuep = nullptr;
5628 : : if (defaultp) {
5629 : : defaultp->dtypep(arrayDtp->subDTypep());
5630 : : defaultValuep = patternMemberValueIterate(defaultp);
5631 : : }
5632 : : AstNodeExpr* newp = new AstConsAssoc{nodep->fileline(), defaultValuep};
5633 : : newp->dtypeFrom(arrayDtp);
5634 : : for (AstPatMember* patp = VN_AS(nodep->itemsp(), PatMember); patp;
5635 : : patp = VN_AS(patp->nextp(), PatMember)) {
5636 : : patp->dtypep(arrayDtp->subDTypep());
5637 : : AstNodeExpr* const valuep = patternMemberValueIterate(patp);
5638 : : AstNode* keyp;
5639 : : if (patp->varrefp()) {
5640 : : keyp = patp->varrefp();
5641 : : } else {
5642 : : keyp = patp->keyp();
5643 : : }
5644 : : if (!keyp) {
5645 : : patp->v3error("Missing pattern key (need an expression then a ':')");
5646 : : keyp = new AstConst{nodep->fileline(), AstConst::Signed32{}, 0};
5647 : : } else {
5648 : : keyp->unlinkFrBack();
5649 : : }
5650 : : AstSetAssoc* const newap = new AstSetAssoc{nodep->fileline(), newp, keyp, valuep};
5651 : : newap->dtypeFrom(arrayDtp);
5652 : : newp = newap;
5653 : : }
5654 : : nodep->replaceWith(newp);
5655 : : // UINFOTREE(9, newp, "", "apat-out");
5656 : : }
5657 : : void patternWildcard(AstPattern* nodep, AstWildcardArrayDType* arrayDtp,
5658 : : AstPatMember* defaultp) {
5659 : : AstNodeExpr* defaultValuep = nullptr;
5660 : : if (defaultp) {
5661 : : defaultp->dtypep(arrayDtp->subDTypep());
5662 : : defaultValuep = patternMemberValueIterate(defaultp);
5663 : : }
5664 : : AstNodeExpr* newp = new AstConsWildcard{nodep->fileline(), defaultValuep};
5665 : : newp->dtypeFrom(arrayDtp);
5666 : : for (AstPatMember* patp = VN_AS(nodep->itemsp(), PatMember); patp;
5667 : : patp = VN_AS(patp->nextp(), PatMember)) {
5668 : : patp->dtypep(arrayDtp->subDTypep());
5669 : : AstNodeExpr* const valuep = patternMemberValueIterate(patp);
5670 : : AstNode* const keyp = patp->keyp();
5671 : : AstSetWildcard* const newap
5672 : : = new AstSetWildcard{nodep->fileline(), newp, keyp->unlinkFrBack(), valuep};
5673 : : newap->dtypeFrom(arrayDtp);
5674 : : newp = newap;
5675 : : }
5676 : : nodep->replaceWith(newp);
5677 : : // UINFOTREE(9, newp, "", "apat-out");
5678 : : }
5679 : : void patternDynArray(AstPattern* nodep, AstDynArrayDType* arrayp, AstPatMember* defaultp) {
5680 : : AstNodeExpr* newp = new AstConsDynArray{nodep->fileline()};
5681 : : newp->dtypeFrom(arrayp);
5682 : : for (AstPatMember* patp = VN_AS(nodep->itemsp(), PatMember); patp;
5683 : : patp = VN_AS(patp->nextp(), PatMember)) {
5684 : : patp->dtypep(arrayp->subDTypep());
5685 : : AstNodeExpr* const rhsp = patternMemberValueIterate(patp);
5686 : : const bool rhsIsValue
5687 : : = AstNode::computeCastable(rhsp->dtypep(), arrayp->subDTypep(), nullptr)
5688 : : .isAssignable();
5689 : : AstConsDynArray* const newap
5690 : : = new AstConsDynArray{nodep->fileline(), rhsIsValue, rhsp, false, newp};
5691 : : newap->dtypeFrom(arrayp);
5692 : : newp = newap;
5693 : : }
5694 : : nodep->replaceWith(newp);
5695 : : // UINFOTREE(9, newp, "", "apat-out");
5696 : : }
5697 : : void patternQueue(AstPattern* nodep, AstQueueDType* arrayp, AstPatMember* defaultp) {
5698 : : AstNodeExpr* newp = new AstConsQueue{nodep->fileline()};
5699 : : newp->dtypeFrom(arrayp);
5700 : : for (AstPatMember* patp = VN_AS(nodep->itemsp(), PatMember); patp;
5701 : : patp = VN_AS(patp->nextp(), PatMember)) {
5702 : : patp->dtypep(arrayp->subDTypep());
5703 : : AstNodeExpr* const rhsp = patternMemberValueIterate(patp);
5704 : : const bool rhsIsDirect
5705 : : = AstNode::computeCastable(rhsp->dtypep(), arrayp, nullptr).isAssignable();
5706 : : const bool rhsIsValue
5707 : : = AstNode::computeCastable(rhsp->dtypep(), arrayp->subDTypep(), nullptr)
5708 : : .isAssignable();
5709 : : AstConsQueue* const newap = new AstConsQueue{
5710 : : nodep->fileline(), !rhsIsDirect && rhsIsValue, rhsp, false, newp};
5711 : : newap->dtypeFrom(arrayp);
5712 : : newp = newap;
5713 : : }
5714 : : nodep->replaceWith(newp);
5715 : : // UINFOTREE(9, newp, "", "apat-out");
5716 : : }
5717 : : void patternBasic(AstPattern* nodep, AstNodeDType* vdtypep, AstPatMember* defaultp) {
5718 : : const AstBasicDType* bdtypep = VN_AS(vdtypep, BasicDType);
5719 : : const VNumRange range = bdtypep->declRange();
5720 : : PatVecMap patmap = patVectorMap(nodep, range);
5721 : : UINFO(9, "ent " << range.hi() << " to " << range.lo());
5722 : : AstNodeExpr* newp = nullptr;
5723 : : for (int ent = range.hi(); ent >= range.lo(); --ent) {
5724 : : AstPatMember* newpatp = nullptr;
5725 : : AstPatMember* patp = nullptr;
5726 : : const auto it = patmap.find(ent);
5727 : : if (it == patmap.end()) {
5728 : : if (defaultp) {
5729 : : newpatp = defaultp->cloneTree(false);
5730 : : patp = newpatp;
5731 : : } else {
5732 : : nodep->v3error("Assignment pattern missed initializing elements: " << ent);
5733 : : }
5734 : : } else {
5735 : : patp = it->second;
5736 : : patmap.erase(it);
5737 : : }
5738 : : if (patp) {
5739 : : // Determine initial values
5740 : : vdtypep = nodep->findBitDType();
5741 : : patp->dtypep(vdtypep);
5742 : : AstNodeExpr* const valuep = patternMemberValueIterate(patp);
5743 : : { // Packed. Convert to concat for now.
5744 : : if (!newp) {
5745 : : newp = valuep;
5746 : : } else {
5747 : : AstConcat* const concatp = new AstConcat{patp->fileline(), newp, valuep};
5748 : : newp = concatp;
5749 : : newp->dtypeSetLogicSized(concatp->lhsp()->width()
5750 : : + concatp->rhsp()->width(),
5751 : : nodep->dtypep()->numeric());
5752 : : }
5753 : : }
5754 : : }
5755 : : if (newpatp) VL_DO_DANGLING(pushDeletep(newpatp), newpatp);
5756 : : }
5757 : : if (!patmap.empty()) nodep->v3error("Assignment pattern with too many elements");
5758 : : if (newp) {
5759 : : nodep->replaceWith(newp);
5760 : : } else {
5761 : : nodep->v3error("Assignment pattern with no members");
5762 : : }
5763 : : // UINFOTREE(9, newp, "", "apat-out");
5764 : : }
5765 : : AstNodeExpr* patternMemberValueIterate(AstPatMember* patp) {
5766 : : // Determine values - might be another InitArray
5767 : : userIterate(patp, WidthVP{patp->dtypep(), BOTH}.p());
5768 : : // Convert to InitArray or constify immediately
5769 : : AstNodeExpr* valuep = patp->lhssp()->unlinkFrBack();
5770 : : if (VN_IS(valuep, Const)) {
5771 : : // Forming a AstConcat will cause problems with
5772 : : // unsized (uncommitted sized) constants
5773 : : if (AstConst* const newp = V3WidthCommit::newIfConstCommitSize(VN_AS(valuep, Const))) {
5774 : : VL_DO_DANGLING(pushDeletep(valuep), valuep);
5775 : : valuep = newp;
5776 : : }
5777 : : }
5778 : : return valuep;
5779 : : }
5780 : :
5781 : : static void checkEventAssignment(const AstNodeAssign* const asgnp) {
5782 : : string unsupEvtAsgn;
5783 : : if (!usesDynamicScheduler(asgnp->lhsp())) unsupEvtAsgn = "to";
5784 : : if (VN_IS(asgnp->rhsp(), CReset)) return;
5785 : : if (asgnp->rhsp()->dtypep()->isEvent() && !usesDynamicScheduler(asgnp->rhsp())) {
5786 : : unsupEvtAsgn += (unsupEvtAsgn.empty() ? "from" : " and from");
5787 : : }
5788 : : if (!unsupEvtAsgn.empty()) {
5789 : : asgnp->v3warn(E_UNSUPPORTED, "Assignment "
5790 : : << unsupEvtAsgn
5791 : : << " event in statically scheduled context.\n"
5792 : : << asgnp->warnMore()
5793 : : << "Static event "
5794 : : "scheduling won't be able to handle this.\n"
5795 : : << asgnp->warnMore()
5796 : : << "... Suggest move the event into a "
5797 : : "completely dynamic context, eg. a class, and "
5798 : : "reference it only from such context.");
5799 : : }
5800 : : }
5801 : :
5802 : : static bool usesDynamicScheduler(AstNode* nodep) {
5803 : : UASSERT_OBJ(nodep->dtypep()->isEvent(), nodep, "Node does not have an event dtype");
5804 : : while (true) {
5805 : : AstVarRef* const vrefp = VN_CAST(nodep, VarRef);
5806 : : if (vrefp) return usesDynamicScheduler(vrefp);
5807 : : if (VN_IS(nodep, MemberSel)) {
5808 : : return true;
5809 : : } else if (AstNodeSel* selp = VN_CAST(nodep, NodeSel)) {
5810 : : nodep = selp->fromp();
5811 : : } else {
5812 : : return false;
5813 : : }
5814 : : }
5815 : : }
5816 : :
5817 : : static bool usesDynamicScheduler(AstVarRef* vrefp) {
5818 : : return VN_IS(vrefp->classOrPackagep(), Class) || vrefp->varp()->isFuncLocal();
5819 : : }
5820 : :
5821 : : void visit(AstPatMember* nodep) override {
5822 : : AstNodeDType* const vdtypep = m_vup->dtypeNullp();
5823 : : UASSERT_OBJ(vdtypep, nodep, "Pattern member type not assigned by AstPattern visitor");
5824 : : nodep->dtypep(vdtypep);
5825 : : UINFO(9, " PATMEMBER " << nodep);
5826 : : UASSERT_OBJ(!nodep->lhssp()->nextp(), nodep,
5827 : : "PatMember value should be singular w/replicates removed");
5828 : : // Need to propagate assignment type downwards, even on prelim
5829 : : userIterateChildren(nodep, WidthVP{nodep->dtypep(), PRELIM}.p());
5830 : : iterateCheck(nodep, "Pattern value", nodep->lhssp(), ASSIGN, FINAL, vdtypep, EXTEND_LHS);
5831 : : }
5832 : : int visitPatMemberRep(AstPatMember* nodep) {
5833 : : uint32_t times = 1;
5834 : : if (nodep->repp()) { // else repp()==nullptr shorthand for rep count 1
5835 : : iterateCheckSizedSelf(nodep, "LHS", nodep->repp(), SELF, BOTH);
5836 : : V3Const::constifyParamsEdit(nodep->repp()); // repp may change
5837 : : const AstConst* const constp = VN_CAST(nodep->repp(), Const);
5838 : : if (!constp) {
5839 : : nodep->v3error("Replication value isn't a constant.");
5840 : : times = 0;
5841 : : } else {
5842 : : times = constp->toUInt();
5843 : : }
5844 : : if (times == 0) {
5845 : : nodep->v3error("Pattern replication value of 0 is not legal.");
5846 : : times = 1;
5847 : : }
5848 : : nodep->repp()
5849 : : ->unlinkFrBackWithNext()
5850 : : ->deleteTree(); // Done with replicate before cloning
5851 : : }
5852 : : return times;
5853 : : }
5854 : :
5855 : : void visit(AstPropSpec* nodep) override {
5856 : : VL_RESTORER(m_seqUnsupp);
5857 : : VL_RESTORER(m_hasSExpr);
5858 : : assertAtExpr(nodep);
5859 : : if (m_vup->prelim()) { // First stage evaluation
5860 : : iterateCheckBool(nodep, "Property", nodep->propp(), BOTH);
5861 : : userIterateAndNext(nodep->sensesp(), nullptr);
5862 : : if (nodep->disablep()) {
5863 : : iterateCheckBool(nodep, "Disable", nodep->disablep(),
5864 : : BOTH); // it's like an if() condition.
5865 : : }
5866 : : nodep->dtypeSetBit();
5867 : : }
5868 : : }
5869 : :
5870 : : bool firstNewStatementOkRecurse(AstNode* nodep) {
5871 : : if (AstVar* const varp = VN_CAST(nodep, Var)) {
5872 : : if (!varp->valuep() || VN_CAST(varp->valuep(), Const) || varp->isIO()) return true;
5873 : : }
5874 : : if (AstAssign* const aitemp = VN_CAST(nodep, Assign)) {
5875 : : if (VN_IS(aitemp->rhsp(), Const) || VN_IS(aitemp->rhsp(), CReset)) return true;
5876 : : }
5877 : : if (AstInitialStaticStmt* const aitemp = VN_CAST(nodep, InitialStaticStmt)) {
5878 : : for (AstNode* stmtp = aitemp->stmtsp(); stmtp; stmtp = stmtp->nextp()) {
5879 : : if (!firstNewStatementOkRecurse(stmtp)) return false;
5880 : : }
5881 : : return true;
5882 : : }
5883 : : if (AstInitialAutomaticStmt* const aitemp = VN_CAST(nodep, InitialAutomaticStmt)) {
5884 : : for (AstNode* stmtp = aitemp->stmtsp(); stmtp; stmtp = stmtp->nextp()) {
5885 : : if (!firstNewStatementOkRecurse(stmtp)) return false;
5886 : : }
5887 : : return true;
5888 : : }
5889 : : return false;
5890 : : }
5891 : :
5892 : : //--------------------
5893 : : // Top levels
5894 : :
5895 : : template <typename CaseItem>
5896 : : void handleCaseType(AstNode* casep, AstNodeExpr* exprp, CaseItem* itemsp) {
5897 : : AstAttrOf* const exprap = VN_CAST(exprp, AttrOf);
5898 : : if (!exprap) return;
5899 : : if (exprap->attrType() != VAttrType::TYPEID) return;
5900 : :
5901 : : const AstNodeDType* const exprDtp = exprap->dtypep();
5902 : : UINFO(9, "case type exprDtp " << exprDtp);
5903 : : // V3Param may have a pointer to this case statement, and we need
5904 : : // dotted references to remain properly named, so rather than
5905 : : // removing we convert it to a "normal" expression "case (1) ..."
5906 : : FileLine* const newfl = casep->fileline();
5907 : : newfl->warnOff(V3ErrorCode::CASEINCOMPLETE, true); // Side effect of transform
5908 : : newfl->warnOff(V3ErrorCode::CASEOVERLAP, true); // Side effect of transform
5909 : : casep->fileline(newfl);
5910 : : for (CaseItem* itemp = itemsp; itemp; itemp = AstNode::as<CaseItem>(itemp->nextp())) {
5911 : : if (itemp->isDefault()) continue;
5912 : : bool hit = false;
5913 : : for (AstNode* condp = itemp->condsp(); condp; condp = condp->nextp()) {
5914 : : const AstAttrOf* const condAttrp = VN_CAST(condp, AttrOf);
5915 : : if (!condAttrp) {
5916 : : condp->v3error("Case(type) statement requires items that have type() items");
5917 : : } else {
5918 : : AstNodeDType* const condDtp = condAttrp->dtypep();
5919 : : if (AstNode::computeCastable(exprDtp, condDtp, casep) == VCastable::SAMEISH) {
5920 : : hit = true;
5921 : : break;
5922 : : }
5923 : : }
5924 : : }
5925 : : pushDeletep(itemp->condsp()->unlinkFrBackWithNext());
5926 : : // Item condition becomes constant 1 if hits else 0
5927 : : itemp->addCondsp(new AstConst{newfl, AstConst::BitTrue{}, hit});
5928 : : }
5929 : : exprap->replaceWith(new AstConst{newfl, AstConst::BitTrue{}});
5930 : : VL_DO_DANGLING(pushDeletep(exprap), exprap);
5931 : : }
5932 : :
5933 : : template <typename CaseItem>
5934 : : void handleCase(AstNode* casep, AstNodeExpr* exprp, CaseItem* itemsp) {
5935 : : // IEEE-2012 12.5:
5936 : : // Width: MAX(expr, all items)
5937 : : // Signed: Only if expr, and all items signed
5938 : : // Take width as maximum across all items, if any is real whole thing is real
5939 : : AstNodeDType* subDTypep = exprp->dtypep();
5940 : : for (CaseItem* itemp = itemsp; itemp; itemp = AstNode::as<CaseItem>(itemp->nextp())) {
5941 : : for (AstNode* condp = itemp->condsp(); condp; condp = condp->nextp()) {
5942 : : if (condp->dtypep() == subDTypep) continue;
5943 : :
5944 : : if (condp->dtypep()->isDouble() || subDTypep->isDouble()) {
5945 : : subDTypep = casep->findDoubleDType();
5946 : : } else if (condp->dtypep()->isString() || subDTypep->isString()) {
5947 : : subDTypep = casep->findStringDType();
5948 : : } else {
5949 : : const int width = std::max(subDTypep->width(), condp->width());
5950 : : const int mwidth = std::max(subDTypep->widthMin(), condp->widthMin());
5951 : : const bool issigned = subDTypep->isSigned() && condp->isSigned();
5952 : : subDTypep = casep->findLogicDType(width, mwidth, VSigning::fromBool(issigned));
5953 : : }
5954 : : }
5955 : : }
5956 : :
5957 : : // Apply width
5958 : : iterateCheck(casep, "Case expression", exprp, CONTEXT_DET, FINAL, subDTypep, EXTEND_EXP);
5959 : : for (CaseItem* itemp = itemsp; itemp; itemp = AstNode::as<CaseItem>(itemp->nextp())) {
5960 : : for (AstNode *nextcp, *condp = itemp->condsp(); condp; condp = nextcp) {
5961 : : nextcp = condp->nextp(); // Final may cause the node to get replaced
5962 : : iterateCheck(casep, "Case Item", condp, CONTEXT_DET, FINAL, subDTypep, EXTEND_LHS);
5963 : : }
5964 : : }
5965 : : }
5966 : :
5967 : : void visit(AstGenCase* nodep) override {
5968 : : assertAtStatement(nodep);
5969 : : // Type check expression and case item conditions, but not bodies
5970 : : userIterateAndNext(nodep->exprp(), WidthVP{CONTEXT_DET, PRELIM}.p());
5971 : : for (AstGenCaseItem *nextip, *itemp = nodep->itemsp(); itemp; itemp = nextip) {
5972 : : // Prelim may cause the node to get replaced, pick up next up front
5973 : : nextip = VN_AS(itemp->nextp(), GenCaseItem);
5974 : : for (AstNode *nextcp, *condp = itemp->condsp(); condp; condp = nextcp) {
5975 : : // Prelim may cause the node to get replaced, pick up next up front
5976 : : nextcp = condp->nextp();
5977 : : VL_DO_DANGLING(userIterate(condp, WidthVP{CONTEXT_DET, PRELIM}.p()), condp);
5978 : : }
5979 : : }
5980 : : // Deal with case(type(data_type))
5981 : : handleCaseType(nodep, nodep->exprp(), nodep->itemsp());
5982 : : // Type check
5983 : : handleCase(nodep, nodep->exprp(), nodep->itemsp());
5984 : : }
5985 : : void visit(AstGenFor* nodep) override {
5986 : : assertAtStatement(nodep);
5987 : : userIterateAndNext(nodep->initsp(), nullptr);
5988 : : iterateCheckBool(nodep, "For Test Condition", nodep->condp(), BOTH);
5989 : : userIterateAndNext(nodep->incsp(), nullptr);
5990 : : }
5991 : : void visit(AstGenIf* nodep) override {
5992 : : assertAtStatement(nodep);
5993 : : iterateCheckBool(nodep, "If", nodep->condp(), BOTH);
5994 : : }
5995 : :
5996 : : void visit(AstCase* nodep) override {
5997 : : assertAtStatement(nodep);
5998 : : // Type check expression case item conditions and bodies
5999 : : userIterateAndNext(nodep->exprp(), WidthVP{CONTEXT_DET, PRELIM}.p());
6000 : : for (AstCaseItem *nextip, *itemp = nodep->itemsp(); itemp; itemp = nextip) {
6001 : : nextip = VN_AS(itemp->nextp(), CaseItem); // Prelim may cause the node to get replaced
6002 : : userIterateAndNext(itemp->stmtsp(), nullptr);
6003 : : for (AstNode *nextcp, *condp = itemp->condsp(); condp; condp = nextcp) {
6004 : : nextcp = condp->nextp(); // Prelim may cause the node to get replaced
6005 : : VL_DO_DANGLING(userIterate(condp, WidthVP{CONTEXT_DET, PRELIM}.p()), condp);
6006 : : }
6007 : : }
6008 : : // Deal with case(type(data_type))
6009 : : handleCaseType(nodep, nodep->exprp(), nodep->itemsp());
6010 : : // Type check
6011 : : handleCase(nodep, nodep->exprp(), nodep->itemsp());
6012 : : }
6013 : : void visit(AstRandCase* nodep) override {
6014 : : // IEEE says each item is a int (32-bits), and sizes are based on natural sizing,
6015 : : // but we'll sum to a 64-bit number then math is faster.
6016 : : assertAtStatement(nodep);
6017 : : AstNodeDType* const itemDTypep = nodep->findUInt32DType();
6018 : : for (AstCaseItem *nextip, *itemp = nodep->itemsp(); itemp; itemp = nextip) {
6019 : : nextip = VN_AS(itemp->nextp(), CaseItem); // Prelim may cause the node to get replaced
6020 : : userIterateAndNext(itemp->stmtsp(), nullptr);
6021 : : for (AstNode *nextcp, *condp = itemp->condsp(); condp; condp = nextcp) {
6022 : : nextcp = condp->nextp(); // Prelim may cause the node to get replaced
6023 : : iterateCheckTyped(itemp, "Randcase Item", condp, itemDTypep, BOTH);
6024 : : VL_DANGLING(condp); // Might have been replaced
6025 : : }
6026 : : VL_DANGLING(itemp); // Might have been replaced
6027 : : }
6028 : : }
6029 : :
6030 : : void visit(AstLoop* nodep) override {
6031 : : UASSERT_OBJ(!nodep->contsp(), nodep, "'contsp' only used before LinkJump");
6032 : : assertAtStatement(nodep);
6033 : : userIterateAndNext(nodep->stmtsp(), nullptr);
6034 : : }
6035 : : void visit(AstLoopTest* nodep) override {
6036 : : assertAtStatement(nodep);
6037 : : iterateCheckBool(nodep, "Loop Condition", nodep->condp(), BOTH);
6038 : : }
6039 : : void visit(AstRepeat* nodep) override { nodep->v3fatalSrc("'repeat' missed in LinkJump"); }
6040 : : void visit(AstNodeIf* nodep) override {
6041 : : assertAtStatement(nodep);
6042 : : // UINFOTREE(1, nodep, "", "IfPre");
6043 : : userIterateAndNext(nodep->thensp(), nullptr);
6044 : : userIterateAndNext(nodep->elsesp(), nullptr);
6045 : : iterateCheckBool(nodep, "If", nodep->condp(), BOTH); // it's like an if() condition.
6046 : : // UINFOTREE(1, nodep, "", "IfOut");
6047 : : }
6048 : : void visit(AstExprStmt* nodep) override {
6049 : : userIterateAndNext(nodep->stmtsp(), nullptr);
6050 : : // expected result is same as parent's expected result
6051 : : userIterateAndNext(nodep->resultp(), m_vup);
6052 : : nodep->dtypeFrom(nodep->resultp());
6053 : : }
6054 : : void visit(AstNodeForeach* nodep) override {
6055 : : if (nodep->didWidth()) return;
6056 : : nodep->didWidth(true);
6057 : : const AstForeachHeader* const headerp = nodep->headerp();
6058 : : // UINFOTREE(1, nodep, "", "foreach-old");
6059 : : userIterateAndNext(headerp->fromp(), WidthVP{SELF, BOTH}.p());
6060 : : AstNodeExpr* const fromp = headerp->fromp();
6061 : : UASSERT_OBJ(fromp->dtypep(), fromp, "Missing data type");
6062 : : AstNodeDType* fromDtp = fromp->dtypep()->skipRefp();
6063 : : // Major dimension first
6064 : : for (AstNode *argsp = headerp->elementsp(), *next_argsp; argsp; argsp = next_argsp) {
6065 : : next_argsp = argsp->nextp();
6066 : : const bool empty = VN_IS(argsp, Empty);
6067 : : AstVar* const varp = VN_CAST(argsp, Var);
6068 : : UASSERT_OBJ(varp || empty, argsp, "Missing foreach loop variable");
6069 : : if (varp) varp->usedLoopIdx(true);
6070 : : if (!fromDtp) {
6071 : : argsp->v3error("foreach loop variables exceed number of indices of array");
6072 : : VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
6073 : : return;
6074 : : }
6075 : : fromDtp = fromDtp->skipRefp();
6076 : : UINFO(9, "- foreachArg " << argsp);
6077 : : UINFO(9, "- from on " << fromp);
6078 : : UINFO(9, "- from dtp " << fromDtp);
6079 : :
6080 : : if (VN_IS(fromDtp, NodeArrayDType) || VN_IS(fromDtp, DynArrayDType)
6081 : : || VN_IS(fromDtp, QueueDType)) {
6082 : : // Nothing special here
6083 : : } else if (AstBasicDType* const adtypep = VN_CAST(fromDtp, BasicDType)) {
6084 : : if (!adtypep->isString() && !adtypep->isRanged()) {
6085 : : argsp->v3error("Illegal 'foreach' loop on " << fromDtp->prettyTypeName()
6086 : : << " data type");
6087 : : VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
6088 : : return;
6089 : : }
6090 : : } else if (const AstAssocArrayDType* const adtypep
6091 : : = VN_CAST(fromDtp, AssocArrayDType)) {
6092 : : varp->dtypeFrom(adtypep->keyDTypep());
6093 : : } else {
6094 : : argsp->v3error("Illegal 'foreach' loop on " << fromDtp->prettyTypeName()
6095 : : << " data type");
6096 : : VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
6097 : : return;
6098 : : }
6099 : : fromDtp = fromDtp->subDTypep();
6100 : : }
6101 : : // The parser validates we don't have "foreach (array[,,,])"
6102 : : AstNode* const bodyp = nodep->bodyp();
6103 : : userIterateAndNext(bodyp, nullptr);
6104 : : if (AstForeach* const loopp = VN_CAST(nodep, Foreach)) {
6105 : : VL_DO_DANGLING2(V3Begin::convertToWhile(loopp), loopp, nodep);
6106 : : return;
6107 : : }
6108 : : }
6109 : :
6110 : : void visit(AstNodeAssign* nodep) override {
6111 : : // IEEE-2012 10.7, 11.8.2, 11.8.3, 11.5: (Careful of 11.8.1 which is
6112 : : // only one step; final dtype depends on assign LHS.)
6113 : : // Determine RHS type width and signing
6114 : : // Propagate type down to *non-self-determined* operators
6115 : : // Real propagates only across one operator if one side is real -
6116 : : // handled in each visitor.
6117 : : // Then LHS sign-extends only if *RHS* is signed
6118 : : assertAtStatement(nodep);
6119 : : {
6120 : : // UINFOTREE(1, nodep, "", "assin:");
6121 : : userIterateAndNext(nodep->lhsp(), WidthVP{SELF, BOTH}.p());
6122 : : UASSERT_OBJ(nodep->lhsp()->dtypep(), nodep, "How can LHS be untyped?");
6123 : : UASSERT_OBJ(nodep->lhsp()->dtypep()->widthSized(), nodep, "How can LHS be unsized?");
6124 : : nodep->dtypeFrom(nodep->lhsp());
6125 : : //
6126 : : // AstPattern needs to know the proposed data type of the lhs, so pass on the prelim
6127 : : // UINFOTREE(1, nodep, "", "assrhs");
6128 : : userIterateAndNext(nodep->rhsp(), WidthVP{nodep->dtypep(), PRELIM}.p());
6129 : : //
6130 : : // UINFOTREE(1, nodep, "", "assign");
6131 : : AstNodeDType* const lhsDTypep
6132 : : = nodep->lhsp()->dtypep(); // Note we use rhsp for context determined
6133 : :
6134 : : // Check width of stream and wrap if needed
6135 : : if (AstNodeStream* const streamp = VN_CAST(nodep->rhsp(), NodeStream)) {
6136 : : AstNodeDType* const lhsDTypeSkippedRefp = lhsDTypep->skipRefp();
6137 : : const int lwidth = widthUnpacked(lhsDTypeSkippedRefp);
6138 : : const int rwidth = widthUnpacked(streamp->lhsp()->dtypep()->skipRefp());
6139 : : if (lwidth != 0 && lwidth < rwidth) {
6140 : : nodep->v3widthWarn(lwidth, rwidth,
6141 : : "Target fixed size variable ("
6142 : : << lwidth << " bits) is narrower than the stream ("
6143 : : << rwidth << " bits) (IEEE 1800-2023 11.4.14)");
6144 : : }
6145 : : if (VN_IS(lhsDTypeSkippedRefp, UnpackArrayDType)) {
6146 : : streamp->unlinkFrBack();
6147 : : nodep->rhsp(new AstCvtPackedToArray{streamp->fileline(), streamp,
6148 : : lhsDTypeSkippedRefp});
6149 : : }
6150 : : }
6151 : : if (const AstNodeStream* const streamp = VN_CAST(nodep->lhsp(), NodeStream)) {
6152 : : const AstNodeDType* const rhsDTypep = nodep->rhsp()->dtypep()->skipRefp();
6153 : : const int lwidth = widthUnpacked(streamp->lhsp()->dtypep()->skipRefp());
6154 : : const int rwidth = widthUnpacked(rhsDTypep);
6155 : : if (rwidth != 0 && rwidth < lwidth) {
6156 : : nodep->v3widthWarn(lwidth, rwidth,
6157 : : "Stream target requires "
6158 : : << lwidth
6159 : : << " bits, but source expression only provides "
6160 : : << rwidth << " bits (IEEE 1800-2023 11.4.14.3)");
6161 : : }
6162 : : if (VN_IS(rhsDTypep, UnpackArrayDType)) {
6163 : : AstNodeExpr* const rhsp = nodep->rhsp()->unlinkFrBack();
6164 : : nodep->rhsp(
6165 : : new AstCvtArrayToPacked{rhsp->fileline(), rhsp, streamp->dtypep()});
6166 : : }
6167 : : }
6168 : :
6169 : : // IEEE 1800-2023 7.6: For unpacked arrays to be assignment compatible,
6170 : : // the element types shall be equivalent (IEEE 1800-2023 6.22.2).
6171 : : // Note: Streaming operators and string literals have implicit conversion rules.
6172 : : if (nodep->rhsp()->dtypep()) { // May be null on earlier errors
6173 : : const AstNodeDType* const lhsDtp = lhsDTypep->skipRefp();
6174 : : const AstNodeDType* const rhsDtp = nodep->rhsp()->dtypep()->skipRefp();
6175 : : // Only check if number of states match for unpacked array to unpacked array
6176 : : // assignments
6177 : : const bool lhsIsUnpackArray = lhsDtp->isNonPackedArray();
6178 : : const bool rhsIsUnpackArray = rhsDtp->isNonPackedArray();
6179 : : if (lhsIsUnpackArray && rhsIsUnpackArray) {
6180 : : if (lhsDtp->isFourstate() != rhsDtp->isFourstate()) {
6181 : : nodep->v3error("Assignment between 2-state and 4-state types requires "
6182 : : "equivalent element types (IEEE 1800-2023 6.22.2, 7.6)\n"
6183 : : << nodep->warnMore()
6184 : : << "... Left-hand type: " << lhsDtp->prettyDTypeNameQ()
6185 : : << lhsDtp->stateDTypeName() << "\n"
6186 : : << nodep->warnMore() << "... Right-hand type: "
6187 : : << rhsDtp->prettyDTypeNameQ() << rhsDtp->stateDTypeName());
6188 : : }
6189 : : }
6190 : : checkUnpackedArrayAssignmentCompatible<AstNodeVarRef, AstNodeVarRef>(
6191 : : nodep, VN_CAST(nodep->lhsp(), NodeVarRef), VN_CAST(nodep->rhsp(), NodeVarRef));
6192 : : }
6193 : :
6194 : : iterateCheckAssign(nodep, "Assign RHS", nodep->rhsp(), FINAL, lhsDTypep);
6195 : : // UINFOTREE(1, nodep, "", "AssignOut");
6196 : : }
6197 : : if (VN_IS(nodep, AssignForce)) checkForceReleaseLhs(nodep, nodep->lhsp());
6198 : : if (AstNode* const controlp = nodep->timingControlp()) {
6199 : : if (!m_underFork && VN_IS(m_ftaskp, Func)) {
6200 : : controlp->v3error("Timing controls are not legal in functions. Suggest use a task "
6201 : : "(IEEE 1800-2023 13.4.4)");
6202 : : VL_DO_DANGLING(controlp->unlinkFrBackWithNext()->deleteTree(), controlp);
6203 : : } else if (nodep->fileline()->timingOn() && v3Global.opt.timing().isSetTrue()) {
6204 : : iterateNull(controlp);
6205 : : } else {
6206 : : if (nodep->fileline()->timingOn()) {
6207 : : if (v3Global.opt.timing().isSetFalse()) {
6208 : : controlp->v3warn(ASSIGNDLY, "Ignoring timing control on this "
6209 : : "assignment/primitive due to --no-timing");
6210 : : } else {
6211 : : controlp->v3warn(E_NEEDTIMINGOPT,
6212 : : "Use --timing or --no-timing to specify how "
6213 : : "timing controls should be handled");
6214 : : }
6215 : : }
6216 : : VL_DO_DANGLING(controlp->unlinkFrBackWithNext()->deleteTree(), controlp);
6217 : : }
6218 : : }
6219 : : if (VN_IS(nodep->rhsp(), EmptyQueue)) {
6220 : : UINFO(9, "= {} -> .delete(): " << nodep);
6221 : : const AstNodeDType* const lhsDtp = nodep->lhsp()->dtypep()->skipRefp();
6222 : : if (!VN_IS(lhsDtp, QueueDType) && !VN_IS(lhsDtp, DynArrayDType)) {
6223 : : nodep->v3warn(E_UNSUPPORTED,
6224 : : "Unsupported/Illegal: empty queue ('{}') in this assign context");
6225 : : VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
6226 : : return;
6227 : : }
6228 : : AstMethodCall* const newp
6229 : : = new AstMethodCall{nodep->fileline(), nodep->lhsp()->unlinkFrBack(), "delete"};
6230 : : newp->dtypeSetVoid();
6231 : : nodep->replaceWith(newp->makeStmt());
6232 : : VL_DO_DANGLING(pushDeletep(nodep), nodep);
6233 : : // Need to now convert it
6234 : : visit(newp);
6235 : : return;
6236 : : }
6237 : : if (const AstNewDynamic* const dynp = VN_CAST(nodep->rhsp(), NewDynamic)) {
6238 : : UINFO(9, "= new[] -> .resize(): " << nodep);
6239 : : AstCMethodHard* newp;
6240 : : if (!dynp->rhsp()) {
6241 : : newp = new AstCMethodHard{nodep->fileline(), nodep->lhsp()->unlinkFrBack(),
6242 : : VCMethod::DYN_RENEW, dynp->sizep()->unlinkFrBack()};
6243 : : } else {
6244 : : newp = new AstCMethodHard{nodep->fileline(), nodep->lhsp()->unlinkFrBack(),
6245 : : VCMethod::DYN_RENEW_COPY, dynp->sizep()->unlinkFrBack()};
6246 : : newp->addPinsp(dynp->rhsp()->unlinkFrBack());
6247 : : }
6248 : : newp->didWidth(true);
6249 : : newp->protect(false);
6250 : : newp->dtypeSetVoid();
6251 : : nodep->replaceWith(newp->makeStmt());
6252 : : VL_DO_DANGLING(pushDeletep(nodep), nodep);
6253 : : return;
6254 : : }
6255 : :
6256 : : // Queue slice on LHS: q[a:b] = rhs -> q.sliceAssign(a, b, rhs)
6257 : : // The LHS was lowered from AstSelExtract to CMethodHard(DYN_SLICE)
6258 : : // by V3WidthSel; that returns a temporary copy so the assignment is
6259 : : // silently discarded. Transform into a mutating sliceAssign call.
6260 : : if (AstCMethodHard* const slicep = VN_CAST(nodep->lhsp(), CMethodHard)) {
6261 : : VCMethod assignMethod;
6262 : : if (slicep->method() == VCMethod::DYN_SLICE) {
6263 : : assignMethod = VCMethod::DYN_SLICE_ASSIGN;
6264 : : } else if (slicep->method() == VCMethod::DYN_SLICE_BACK_BACK) {
6265 : : assignMethod = VCMethod::DYN_SLICE_ASSIGN_BACK_BACK;
6266 : : } else if (slicep->method() == VCMethod::DYN_SLICE_FRONT_BACK) {
6267 : : assignMethod = VCMethod::DYN_SLICE_ASSIGN_FRONT_BACK;
6268 : : } else {
6269 : : assignMethod = VCMethod::_ENUM_MAX; // not a slice
6270 : : }
6271 : : if (assignMethod != VCMethod::_ENUM_MAX) {
6272 : : UINFO(9, "LHS queue slice -> sliceAssign: " << nodep);
6273 : : AstNodeExpr* const fromp = slicep->fromp()->unlinkFrBack();
6274 : : // Collect existing slice index pins (lsb, msb)
6275 : : AstNodeExpr* const lsbp = slicep->pinsp()->unlinkFrBack();
6276 : : AstNodeExpr* const msbp = slicep->pinsp()->unlinkFrBack();
6277 : : AstNodeExpr* const rhsp = nodep->rhsp()->unlinkFrBack();
6278 : : AstCMethodHard* const newp
6279 : : = new AstCMethodHard{nodep->fileline(), fromp, assignMethod};
6280 : : newp->addPinsp(lsbp);
6281 : : newp->addPinsp(msbp);
6282 : : newp->addPinsp(rhsp);
6283 : : newp->didWidth(true);
6284 : : newp->protect(false);
6285 : : newp->dtypeSetVoid();
6286 : : nodep->replaceWith(newp->makeStmt());
6287 : : VL_DO_DANGLING(pushDeletep(nodep), nodep);
6288 : : return;
6289 : : }
6290 : : }
6291 : :
6292 : : if (nodep->hasDType() && nodep->dtypep()->isEvent()) {
6293 : : checkEventAssignment(nodep);
6294 : : v3Global.setAssignsEvents();
6295 : : }
6296 : : }
6297 : :
6298 : : void visit(AstRSRule* nodep) override {
6299 : : if (nodep->weightp()) iterateCheckUInt32(nodep, "weight", nodep->weightp(), BOTH);
6300 : : userIterateAndNext(nodep->prodlistsp(), nullptr);
6301 : : userIterateAndNext(nodep->weightStmtsp(), nullptr);
6302 : : }
6303 : :
6304 : : void visit(AstRelease* nodep) override {
6305 : : userIterateAndNext(nodep->lhsp(), WidthVP{SELF, BOTH}.p());
6306 : : UASSERT_OBJ(nodep->lhsp()->dtypep(), nodep, "How can LValue be untyped?");
6307 : : UASSERT_OBJ(nodep->lhsp()->dtypep()->widthSized(), nodep, "How can LValue be unsized?");
6308 : : checkForceReleaseLhs(nodep, nodep->lhsp());
6309 : : }
6310 : :
6311 : : static bool isFormatNonNumericArg(const AstNodeDType* dtypep) {
6312 : : dtypep = dtypep->skipRefp();
6313 : : return dtypep->isString() //
6314 : : || VN_IS(dtypep, AssocArrayDType) //
6315 : : || VN_IS(dtypep, WildcardArrayDType) //
6316 : : || VN_IS(dtypep, ClassRefDType) //
6317 : : || VN_IS(dtypep, DynArrayDType) //
6318 : : || VN_IS(dtypep, UnpackArrayDType) //
6319 : : || VN_IS(dtypep, QueueDType) //
6320 : : || (VN_IS(dtypep, NodeUOrStructDType)
6321 : : && !VN_AS(dtypep, NodeUOrStructDType)->packed());
6322 : : }
6323 : :
6324 : : void checkFormatNumericArg(AstNode* argp, char ch) {
6325 : : // IEEE 1800-2023 21.2.1.1 suggests %d of a class is illegal.
6326 : : // Howver UVM tests require %0d print unique identifier of the class.
6327 : : if (std::tolower(ch) == 'd') return;
6328 : : // In contrast, %x of a class reference, causes errors on several simulators.
6329 : : if (isFormatNonNumericArg(argp->dtypep()))
6330 : : argp->v3error(
6331 : : "$display-like format of '%"s + ch + "' illegal with non-numeric argument\n"
6332 : : << argp->warnMore() << "... Suggest use '%s'");
6333 : : }
6334 : :
6335 : : void visit(AstSFormatArg* nodep) override {} // Done by AstSFormatF
6336 : : void visit(AstSFormat* nodep) override {
6337 : : assertAtStatement(nodep);
6338 : : userIterateAndNext(nodep->fmtp(), WidthVP{SELF, BOTH}.p());
6339 : : userIterateAndNext(nodep->lhsp(), WidthVP{SELF, BOTH}.p());
6340 : : }
6341 : : void visit(AstToStringN* nodep) override {
6342 : : // Inserted by V3Width only so we know has been resolved
6343 : : nodep->dtypeSetString();
6344 : : userIterateAndNext(nodep->lhsp(), WidthVP{nodep->lhsp()->dtypep(), BOTH}.p());
6345 : : }
6346 : : void visit(AstSFormatF* nodep) override {
6347 : : // Excludes NodeDisplay, see below
6348 : : if (m_vup && !m_vup->prelim()) return; // Can be called as statement or function
6349 : : if (nodep->didWidthAndSet()) return;
6350 : : //
6351 : : UINFO(9, " Display in " << nodep->text());
6352 : :
6353 : : // Just let all arguments seek their natural sizes
6354 : : userIterateChildren(nodep, WidthVP{SELF, BOTH}.p());
6355 : : if (!nodep->exprFormat()) { // Non-constant format, can't check format string types
6356 : : visit_sformatf_format(nodep);
6357 : : }
6358 : :
6359 : : AstNodeExpr* oldExprsp = nodep->exprsp();
6360 : : if (nodep->exprFormat() && oldExprsp) {
6361 : : VL_DO_DANGLING(iterateCheckString(nodep, "format", nodep->exprsp(), BOTH), oldExprsp);
6362 : : oldExprsp = VN_AS(nodep->exprsp()->nextp(), NodeExpr);
6363 : : }
6364 : : if (oldExprsp) oldExprsp->unlinkFrBackWithNext();
6365 : : while (AstNodeExpr* argp = oldExprsp) {
6366 : : oldExprsp = VN_AS(oldExprsp->nextp(), NodeExpr);
6367 : : if (oldExprsp) oldExprsp->unlinkFrBackWithNext();
6368 : : // Need to record formatAttr's at elaboration time, as later optimizations
6369 : : // may change an argument's data type. Plus need them for runtime formats
6370 : : VFormatAttr formatAttr = VFormatAttr::UNSIGNED;
6371 : : const AstNodeDType* const dtypep = argp ? argp->dtypep()->skipRefp() : nullptr;
6372 : : if (dtypep->isDouble()) {
6373 : : formatAttr = VFormatAttr::DOUBLE;
6374 : : } else if (dtypep->isString()) {
6375 : : formatAttr = VFormatAttr::STRING;
6376 : : } else if (isFormatNonNumericArg(dtypep)) {
6377 : : AstNodeExpr* const newp = new AstToStringN{argp->fileline(), argp};
6378 : : formatAttr = VFormatAttr::COMPLEX;
6379 : : argp = newp;
6380 : : } else if (dtypep->isSigned()) {
6381 : : formatAttr = VFormatAttr::SIGNED;
6382 : : }
6383 : : if (VN_IS(argp, SFormatArg) // Already done
6384 : : || formatAttr.isUnsigned()) { // Save Ast space and imply the AstSFormatArg
6385 : : nodep->addExprsp(argp);
6386 : : } else {
6387 : : nodep->addExprsp(new AstSFormatArg{argp->fileline(), formatAttr, argp});
6388 : : }
6389 : : }
6390 : :
6391 : : UINFO(9, " Display out " << nodep->text());
6392 : : }
6393 : : void visit(AstCReset* nodep) override {
6394 : : assertAtExpr(nodep);
6395 : : nodep->dtypeFrom(m_vup->dtypep());
6396 : : }
6397 : : void visit(AstCReturn* nodep) override { nodep->v3fatalSrc("Should not exist yet"); }
6398 : : void visit(AstConstraintRef* nodep) override { userIterateChildren(nodep, nullptr); }
6399 : : void visit(AstDisplay* nodep) override {
6400 : : assertAtStatement(nodep);
6401 : : if (nodep->filep()) iterateCheckFileDesc(nodep, nodep->filep(), BOTH);
6402 : : // Just let all arguments seek their natural sizes
6403 : : userIterateChildren(nodep, WidthVP{SELF, BOTH}.p());
6404 : : }
6405 : : void visit(AstElabDisplay* nodep) override {
6406 : : assertAtStatement(nodep);
6407 : : // Just let all arguments seek their natural sizes
6408 : : userIterateChildren(nodep, WidthVP{SELF, BOTH}.p());
6409 : : if (!m_paramsOnly) {
6410 : : nodep->foreach([this](AstScopeName* nodep) { //
6411 : : nodep->replaceWith(
6412 : : new AstConst{nodep->fileline(), AstConst::String{}, "<scope-unavailable>"});
6413 : : pushDeletep(nodep);
6414 : : });
6415 : : V3Const::constifyParamsEdit(nodep->fmtp()); // fmtp may change
6416 : : string text = VString::dequotePercent(nodep->fmtp()->text());
6417 : : if (text.empty()) text = "Elaboration system task message (IEEE 1800-2023 20.11)";
6418 : : switch (nodep->displayType()) {
6419 : : case VDisplayType::DT_INFO: nodep->v3warn(USERINFO, text); break;
6420 : : case VDisplayType::DT_ERROR: nodep->v3warn(USERERROR, text); break;
6421 : : case VDisplayType::DT_WARNING: nodep->v3warn(USERWARN, text); break;
6422 : : case VDisplayType::DT_FATAL: nodep->v3warn(USERFATAL, text); break;
6423 : : default: UASSERT_OBJ(false, nodep, "Unexpected elaboration display type");
6424 : : }
6425 : : VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
6426 : : }
6427 : : }
6428 : : void visit(AstDumpCtl* nodep) override {
6429 : : assertAtStatement(nodep);
6430 : : if (nodep->exprp()) iterateCheckString(nodep, "LHS", nodep->exprp(), BOTH);
6431 : : }
6432 : : void visit(AstFOpen* nodep) override {
6433 : : // Although a system function in IEEE, here a statement which sets the file pointer (MCD)
6434 : : iterateCheckString(nodep, "filename", nodep->filenamep(), BOTH);
6435 : : iterateCheckString(nodep, "mode", nodep->modep(), BOTH);
6436 : : nodep->dtypeSetLogicUnsized(32, 1, VSigning::SIGNED); // Spec says integer return
6437 : : }
6438 : : void visit(AstFOpenMcd* nodep) override {
6439 : : iterateCheckString(nodep, "filename", nodep->filenamep(), BOTH);
6440 : : nodep->dtypeSetLogicUnsized(32, 1, VSigning::SIGNED); // Spec says integer return
6441 : : }
6442 : : void visit(AstFClose* nodep) override {
6443 : : assertAtStatement(nodep);
6444 : : iterateCheckFileDesc(nodep, nodep->filep(), BOTH);
6445 : : }
6446 : : void visit(AstFError* nodep) override {
6447 : : assertAtExpr(nodep);
6448 : : if (m_vup->prelim()) {
6449 : : iterateCheckFileDesc(nodep, nodep->filep(), BOTH);
6450 : : // Could be string or packed array
6451 : : userIterateAndNext(nodep->strp(), WidthVP{SELF, BOTH}.p());
6452 : : nodep->dtypeSetLogicUnsized(32, 1, VSigning::SIGNED); // Spec says integer return
6453 : : }
6454 : : }
6455 : : void visit(AstFEof* nodep) override {
6456 : : assertAtExpr(nodep);
6457 : : if (m_vup->prelim()) {
6458 : : iterateCheckFileDesc(nodep, nodep->filep(), BOTH);
6459 : : nodep->dtypeSetLogicUnsized(32, 1, VSigning::SIGNED); // Spec says integer return
6460 : : }
6461 : : }
6462 : : void visit(AstFFlush* nodep) override {
6463 : : assertAtStatement(nodep);
6464 : : if (nodep->filep()) iterateCheckFileDesc(nodep, nodep->filep(), BOTH);
6465 : : }
6466 : : void visit(AstFRewind* nodep) override {
6467 : : iterateCheckFileDesc(nodep, nodep->filep(), BOTH);
6468 : : nodep->dtypeSetLogicUnsized(32, 1, VSigning::SIGNED); // Spec says integer return
6469 : : }
6470 : : void visit(AstFTell* nodep) override {
6471 : : iterateCheckFileDesc(nodep, nodep->filep(), BOTH);
6472 : : nodep->dtypeSetLogicUnsized(32, 1, VSigning::SIGNED); // Spec says integer return
6473 : : }
6474 : : void visit(AstFSeek* nodep) override {
6475 : : iterateCheckFileDesc(nodep, nodep->filep(), BOTH);
6476 : : iterateCheckSigned32(nodep, "$fseek offset", nodep->offset(), BOTH);
6477 : : iterateCheckSigned32(nodep, "$fseek operation", nodep->operation(), BOTH);
6478 : : nodep->dtypeSetLogicUnsized(32, 1, VSigning::SIGNED); // Spec says integer return
6479 : : }
6480 : : void visit(AstFGetC* nodep) override {
6481 : : assertAtExpr(nodep);
6482 : : if (m_vup->prelim()) {
6483 : : iterateCheckFileDesc(nodep, nodep->filep(), BOTH);
6484 : : nodep->dtypeSetLogicUnsized(32, 8, VSigning::SIGNED); // Spec says integer return
6485 : : }
6486 : : }
6487 : : void visit(AstFGetS* nodep) override {
6488 : : assertAtExpr(nodep);
6489 : : if (m_vup->prelim()) {
6490 : : nodep->dtypeSetSigned32(); // Spec says integer return
6491 : : iterateCheckFileDesc(nodep, nodep->filep(), BOTH);
6492 : : userIterateAndNext(nodep->strgp(), WidthVP{SELF, BOTH}.p());
6493 : : }
6494 : : }
6495 : : void visit(AstFUngetC* nodep) override {
6496 : : assertAtExpr(nodep);
6497 : : if (m_vup->prelim()) {
6498 : : iterateCheckFileDesc(nodep, nodep->filep(), BOTH);
6499 : : iterateCheckSigned32(nodep, "$fungetc character", nodep->charp(), BOTH);
6500 : : nodep->dtypeSetLogicUnsized(32, 8, VSigning::SIGNED); // Spec says integer return
6501 : : }
6502 : : }
6503 : : void visit(AstFRead* nodep) override {
6504 : : assertAtExpr(nodep);
6505 : : if (m_vup->prelim()) {
6506 : : nodep->dtypeSetSigned32(); // Spec says integer return
6507 : : userIterateAndNext(nodep->memp(), WidthVP{SELF, BOTH}.p());
6508 : : iterateCheckFileDesc(nodep, nodep->filep(), BOTH);
6509 : : if (nodep->startp()) {
6510 : : iterateCheckSigned32(nodep, "$fread start", nodep->startp(), BOTH);
6511 : : }
6512 : : if (nodep->countp()) {
6513 : : iterateCheckSigned32(nodep, "$fread count", nodep->countp(), BOTH);
6514 : : }
6515 : : }
6516 : : }
6517 : : void visit(AstFScanF* nodep) override {
6518 : : assertAtExpr(nodep);
6519 : : if (m_vup->prelim()) {
6520 : : nodep->dtypeSetSigned32(); // Spec says integer return
6521 : : iterateCheckFileDesc(nodep, nodep->filep(), BOTH);
6522 : : userIterateAndNext(nodep->exprsp(), WidthVP{SELF, BOTH}.p());
6523 : : }
6524 : : }
6525 : : void visit(AstSScanF* nodep) override {
6526 : : assertAtExpr(nodep);
6527 : : if (m_vup->prelim()) {
6528 : : nodep->dtypeSetSigned32(); // Spec says integer return
6529 : : userIterateAndNext(nodep->fromp(), WidthVP{SELF, BOTH}.p());
6530 : : userIterateAndNext(nodep->exprsp(), WidthVP{SELF, BOTH}.p());
6531 : : }
6532 : : }
6533 : : void visit(AstStackTraceF* nodep) override { nodep->dtypeSetString(); }
6534 : : void visit(AstSysIgnore* nodep) override {
6535 : : userIterateAndNext(nodep->exprsp(), WidthVP{SELF, BOTH}.p());
6536 : : }
6537 : : void visit(AstSystemF* nodep) override {
6538 : : assertAtExpr(nodep);
6539 : : if (m_vup->prelim()) {
6540 : : userIterateAndNext(nodep->lhsp(), WidthVP{SELF, BOTH}.p());
6541 : : nodep->dtypeSetSigned32(); // Spec says integer return
6542 : : }
6543 : : }
6544 : : void visit(AstSystemT* nodep) override {
6545 : : assertAtStatement(nodep);
6546 : : userIterateAndNext(nodep->lhsp(), WidthVP{SELF, BOTH}.p());
6547 : : }
6548 : : void visit(AstNodeReadWriteMem* nodep) override {
6549 : : assertAtStatement(nodep);
6550 : : iterateCheckString(nodep, "filename", nodep->filenamep(), BOTH);
6551 : : userIterateAndNext(nodep->memp(), WidthVP{SELF, BOTH}.p());
6552 : : const AstNodeDType* subp = nullptr;
6553 : : if (const AstAssocArrayDType* adtypep
6554 : : = VN_CAST(nodep->memp()->dtypep()->skipRefp(), AssocArrayDType)) {
6555 : : subp = adtypep->subDTypep();
6556 : : if (!adtypep->keyDTypep()->skipRefp()->basicp()
6557 : : || !adtypep->keyDTypep()->skipRefp()->basicp()->keyword().isIntNumeric()) {
6558 : : nodep->memp()->v3error(nodep->verilogKwd()
6559 : : << " address/key must be integral (IEEE 1800-2023 21.4.1)");
6560 : : }
6561 : : } else if (const AstUnpackArrayDType* const adtypep
6562 : : = VN_CAST(nodep->memp()->dtypep()->skipRefp(), UnpackArrayDType)) {
6563 : : subp = adtypep->subDTypep();
6564 : : } else {
6565 : : nodep->memp()->v3warn(E_UNSUPPORTED,
6566 : : "Unsupported: "
6567 : : << nodep->verilogKwd()
6568 : : << " into other than unpacked or associative array");
6569 : : }
6570 : : if (subp
6571 : : && (!subp->skipRefp()->basicp()
6572 : : || !subp->skipRefp()->basicp()->keyword().isIntNumeric())) {
6573 : : nodep->memp()->v3warn(E_UNSUPPORTED,
6574 : : "Unsupported: " << nodep->verilogKwd()
6575 : : << " array values must be integral");
6576 : : }
6577 : : userIterateAndNext(nodep->lsbp(), WidthVP{SELF, BOTH}.p());
6578 : : userIterateAndNext(nodep->msbp(), WidthVP{SELF, BOTH}.p());
6579 : : }
6580 : : void visit(AstTestPlusArgs* nodep) override {
6581 : : assertAtExpr(nodep);
6582 : : if (m_vup->prelim()) {
6583 : : iterateCheckString(nodep, "LHS", nodep->searchp(), BOTH);
6584 : : nodep->dtypeChgWidthSigned(32, 1, VSigning::SIGNED); // Spec says integer return
6585 : : }
6586 : : }
6587 : : void visit(AstValuePlusArgs* nodep) override {
6588 : : assertAtExpr(nodep);
6589 : : if (m_vup->prelim()) {
6590 : : iterateCheckString(nodep, "LHS", nodep->searchp(), BOTH);
6591 : : userIterateAndNext(nodep->outp(), WidthVP{SELF, BOTH}.p());
6592 : : nodep->dtypeChgWidthSigned(32, 1, VSigning::SIGNED); // Spec says integer return
6593 : : }
6594 : : }
6595 : : void visit(AstTimeFormat* nodep) override {
6596 : : assertAtStatement(nodep);
6597 : : if (nodep->unitsp()) iterateCheckSigned32(nodep, "units", nodep->unitsp(), BOTH);
6598 : : if (nodep->precisionp())
6599 : : iterateCheckSigned32(nodep, "precision", nodep->precisionp(), BOTH);
6600 : : if (nodep->suffixp()) iterateCheckString(nodep, "suffix", nodep->suffixp(), BOTH);
6601 : : if (nodep->widthp()) iterateCheckSigned32(nodep, "width", nodep->widthp(), BOTH);
6602 : : }
6603 : : void visit(AstCStmtUser* nodep) override {
6604 : : // Just let all arguments seek their natural sizes
6605 : : assertAtStatement(nodep);
6606 : : userIterateChildren(nodep, WidthVP{SELF, BOTH}.p());
6607 : : }
6608 : : void visit(AstAssert* nodep) override {
6609 : : assertAtStatement(nodep);
6610 : : iterateCheckBool(nodep, "Property", nodep->propp(), BOTH); // it's like an if() condition.
6611 : : userIterateAndNext(nodep->passsp(), nullptr);
6612 : : userIterateAndNext(nodep->failsp(), nullptr);
6613 : : }
6614 : : void visit(AstAssertCtl* nodep) override {
6615 : : assertAtStatement(nodep);
6616 : : userIterateChildren(nodep, WidthVP{SELF, BOTH}.p());
6617 : : }
6618 : : void visit(AstAssertIntrinsic* nodep) override {
6619 : : assertAtStatement(nodep);
6620 : : iterateCheckBool(nodep, "Property", nodep->propp(), BOTH); // it's like an if() condition.
6621 : : userIterateAndNext(nodep->passsp(), nullptr);
6622 : : userIterateAndNext(nodep->failsp(), nullptr);
6623 : : }
6624 : : void visit(AstCover* nodep) override {
6625 : : assertAtStatement(nodep);
6626 : : iterateCheckBool(nodep, "Property", nodep->propp(), BOTH); // it's like an if() condition.
6627 : : userIterateAndNext(nodep->passsp(), nullptr);
6628 : : }
6629 : : void visit(AstRestrict* nodep) override {
6630 : : assertAtStatement(nodep);
6631 : : iterateCheckBool(nodep, "Property", nodep->propp(), BOTH); // it's like an if() condition.
6632 : : }
6633 : : void visit(AstPin* nodep) override {
6634 : : // UINFOTREE(1, nodep, "", "PinPre");
6635 : : // TOP LEVEL NODE
6636 : : if (nodep->modVarp() && nodep->modVarp()->isGParam()) {
6637 : : // Widthing handled as special init() case
6638 : : bool didWidth = false;
6639 : : if (AstPattern* const patternp = VN_CAST(nodep->exprp(), Pattern)) {
6640 : : const AstVar* const modVarp = nodep->modVarp();
6641 : : // Convert BracketArrayDType
6642 : : userIterate(modVarp->childDTypep(),
6643 : : WidthVP{SELF, BOTH}.p()); // May relink pointed to node
6644 : : AstNodeDType* const setDtp = modVarp->childDTypep();
6645 : : if (!patternp->childDTypep()) patternp->childDTypep(setDtp->cloneTree(false));
6646 : : userIterateChildren(nodep, WidthVP{setDtp, BOTH}.p());
6647 : : didWidth = true;
6648 : : }
6649 : : if (!didWidth) userIterateChildren(nodep, WidthVP{SELF, BOTH}.p());
6650 : : } else if (!m_paramsOnly) {
6651 : : if (!nodep->modVarp()->didWidth()) {
6652 : : // Var hasn't been widthed, so make it so.
6653 : : userIterate(nodep->modVarp(), nullptr);
6654 : : }
6655 : : if (!nodep->exprp()) { // No-connect
6656 : : return;
6657 : : }
6658 : : // Very much like like an assignment, but which side is LH/RHS
6659 : : // depends on pin being a in/output/inout.
6660 : : userIterateAndNext(nodep->exprp(), WidthVP{nodep->modVarp()->dtypep(), PRELIM}.p());
6661 : : AstNodeDType* modDTypep = nodep->modVarp()->dtypep();
6662 : : AstNodeDType* conDTypep = nodep->exprp()->dtypep();
6663 : : UASSERT_OBJ(modDTypep, nodep, "Unlinked pin data type");
6664 : : UASSERT_OBJ(conDTypep, nodep, "Unlinked pin data type");
6665 : : modDTypep = modDTypep->skipRefp();
6666 : : conDTypep = conDTypep->skipRefp();
6667 : : AstNodeDType* subDTypep = modDTypep;
6668 : : const int modwidth = modDTypep->width();
6669 : : const int conwidth = conDTypep->width();
6670 : : if (conDTypep == modDTypep // If match, we're golden
6671 : : || similarDTypeRecurse(conDTypep, modDTypep)) {
6672 : : userIterateAndNext(nodep->exprp(), WidthVP{subDTypep, FINAL}.p());
6673 : : } else if (m_cellp->rangep()) {
6674 : : const int numInsts = m_cellp->rangep()->elementsConst();
6675 : : if (conwidth == modwidth) {
6676 : : // Arrayed instants: widths match so connect to each instance
6677 : : subDTypep = conDTypep; // = same expr dtype
6678 : : } else if (conwidth == numInsts * modwidth) {
6679 : : // Arrayed instants: one bit for each of the instants (each
6680 : : // assign is 1 modwidth wide)
6681 : : subDTypep = conDTypep; // = same expr dtype (but numInst*pin_dtype)
6682 : : } else {
6683 : : // Must be a error according to spec
6684 : : // (Because we need to know if to connect to one or all instants)
6685 : : nodep->v3error(ucfirst(nodep->prettyOperatorName())
6686 : : << " as part of a module instance array" << " requires "
6687 : : << modwidth << " or " << modwidth * numInsts
6688 : : << " bits, but connection's "
6689 : : << nodep->exprp()->prettyTypeName() << " generates " << conwidth
6690 : : << " bits. (IEEE 1800-2023 23.3.3)");
6691 : : subDTypep = conDTypep; // = same expr dtype
6692 : : }
6693 : : userIterateAndNext(nodep->exprp(), WidthVP{subDTypep, FINAL}.p());
6694 : : } else {
6695 : : if (nodep->modVarp()->direction() == VDirection::REF) {
6696 : : nodep->v3error("Ref connection "
6697 : : << nodep->modVarp()->prettyNameQ()
6698 : : << " requires matching types;" << " ref requires "
6699 : : << modDTypep->prettyDTypeNameQ()
6700 : : << " data type but connection is "
6701 : : << conDTypep->prettyDTypeNameQ() << " data type.");
6702 : : } else if (nodep->modVarp()->isTristate()) {
6703 : : if (modwidth != conwidth) {
6704 : : // Ideally should call pinReconnectSimple which would tolerate this
6705 : : // then have a conversion warning
6706 : : nodep->v3warn(E_UNSUPPORTED,
6707 : : "Unsupported: " << ucfirst(nodep->prettyOperatorName())
6708 : : << " to inout signal requires " << modwidth
6709 : : << " bits, but connection's "
6710 : : << nodep->exprp()->prettyTypeName()
6711 : : << " generates " << conwidth << " bits.");
6712 : : // otherwise would need some mess to force both sides to proper size
6713 : : }
6714 : : } else if (nodep->modVarp()->direction().isWritable()
6715 : : && ((conDTypep->isDouble() && !modDTypep->isDouble())
6716 : : || (!conDTypep->isDouble() && modDTypep->isDouble()))) {
6717 : : nodep->v3warn(E_UNSUPPORTED,
6718 : : "Unsupported: " << ucfirst(nodep->prettyOperatorName())
6719 : : << " connects real to non-real");
6720 : : }
6721 : :
6722 : : // Check if an interface is connected to a non-interface and vice versa
6723 : : if ((VN_IS(modDTypep, IfaceRefDType) && !VN_IS(conDTypep, IfaceRefDType))
6724 : : || (VN_IS(conDTypep, IfaceRefDType) && !VN_IS(modDTypep, IfaceRefDType))) {
6725 : : nodep->v3error("Illegal " << nodep->prettyOperatorName() << ","
6726 : : << " mismatch between port which is"
6727 : : << (VN_CAST(modDTypep, IfaceRefDType) ? "" : " not")
6728 : : << " an interface," << " and expression which is"
6729 : : << (VN_CAST(conDTypep, IfaceRefDType) ? "" : " not")
6730 : : << " an interface.");
6731 : : }
6732 : :
6733 : : // TODO Simple dtype checking, should be a more general check
6734 : : const AstNodeArrayDType* const exprArrayp = VN_CAST(conDTypep, UnpackArrayDType);
6735 : : const AstNodeArrayDType* const modArrayp = VN_CAST(modDTypep, UnpackArrayDType);
6736 : : if (exprArrayp && modArrayp && VN_IS(exprArrayp->subDTypep(), IfaceRefDType)
6737 : : && exprArrayp->declRange().elements() != modArrayp->declRange().elements()) {
6738 : : const int exprSize = exprArrayp->declRange().elements();
6739 : : const int modSize = modArrayp->declRange().elements();
6740 : : nodep->v3error("Illegal "
6741 : : << nodep->prettyOperatorName() << ","
6742 : : << " mismatch between port which is an interface array of size "
6743 : : << modSize << ","
6744 : : << " and expression which is an interface array of size "
6745 : : << exprSize << ".");
6746 : : UINFO(1, " Related lo: " << modDTypep);
6747 : : UINFO(1, " Related hi: " << conDTypep);
6748 : : } else if ((exprArrayp && !modArrayp) || (!exprArrayp && modArrayp)) {
6749 : : nodep->v3error(
6750 : : "Illegal "
6751 : : << nodep->prettyOperatorName() << "," << " mismatch between port which is"
6752 : : << (modArrayp ? "" : " not") << " an array," << " and expression which is"
6753 : : << (exprArrayp ? "" : " not") << " an array. (IEEE 1800-2023 7.6)");
6754 : : UINFO(1, " Related lo: " << modDTypep);
6755 : : UINFO(1, " Related hi: " << conDTypep);
6756 : : } else {
6757 : : checkUnpackedArrayAssignmentCompatible<AstVar, AstNodeVarRef>(
6758 : : nodep, nodep->modVarp(), VN_CAST(nodep->exprp(), NodeVarRef));
6759 : : UINFO(1, " Related lo: " << modDTypep);
6760 : : UINFO(1, " Related hi: " << conDTypep);
6761 : : }
6762 : : iterateCheckAssign(nodep, "pin connection", nodep->exprp(), FINAL, subDTypep);
6763 : : }
6764 : : }
6765 : : // UINFOTREE(1, nodep, "", "PinOut");
6766 : : }
6767 : : void visit(AstCell* nodep) override {
6768 : : VL_RESTORER(m_cellp);
6769 : : m_cellp = nodep;
6770 : : if (!m_paramsOnly) {
6771 : : if (VN_IS(nodep->modp(), NotFoundModule)) {
6772 : : // We've resolved parameters and hit a module that we couldn't resolve. It's
6773 : : // finally time to report it.
6774 : : // Note only here in V3Width as this is first visitor after V3Dead.
6775 : : nodep->modNameFileline()->v3warn(
6776 : : MODMISSING, "Cannot find file containing module: '"
6777 : : << nodep->modName() << "'\n"
6778 : : << nodep->modNameFileline()->warnContextPrimary()
6779 : : << V3Error::warnAdditionalInfo()
6780 : : << v3Global.opt.filePathLookedMsg(nodep->modNameFileline(),
6781 : : nodep->modName()));
6782 : : }
6783 : : if (nodep->rangep()) userIterateAndNext(nodep->rangep(), WidthVP{SELF, BOTH}.p());
6784 : : userIterateAndNext(nodep->pinsp(), nullptr);
6785 : : }
6786 : : userIterateAndNext(nodep->paramsp(), nullptr);
6787 : : }
6788 : : void visit(AstGatePin* nodep) override {
6789 : : assertAtExpr(nodep);
6790 : : if (m_vup->prelim()) {
6791 : : userIterateAndNext(nodep->rangep(), WidthVP{SELF, BOTH}.p());
6792 : : userIterateAndNext(nodep->exprp(), WidthVP{CONTEXT_DET, PRELIM}.p());
6793 : : nodep->dtypeFrom(nodep->rangep());
6794 : : // Very much like like an pin
6795 : : const AstNodeDType* const pinDTypep = nodep->exprp()->dtypep();
6796 : : const int numInsts = nodep->rangep()->elementsConst();
6797 : : const int modwidth = numInsts;
6798 : : const int pinwidth = pinDTypep->width();
6799 : : if (pinwidth == 1 && modwidth > 1) { // Multiple connections
6800 : : AstNodeDType* const subDTypep = nodep->findLogicDType(1, 1, pinDTypep->numeric());
6801 : : userIterateAndNext(nodep->exprp(), WidthVP{subDTypep, FINAL}.p());
6802 : : AstNode* const newp
6803 : : = new AstReplicate{nodep->fileline(), nodep->exprp()->unlinkFrBack(),
6804 : : static_cast<uint32_t>(numInsts)};
6805 : : nodep->replaceWith(newp);
6806 : : } else {
6807 : : if (pinwidth != modwidth) { // && is not generic interconnect (when supported)
6808 : : nodep->exprp()->v3error("Gate primitive connection expects "
6809 : : << modwidth << " bits "
6810 : : << ((modwidth != 1) ? "or 1 bit "s : ""s)
6811 : : << "on the gate port, but the connection generates "
6812 : : << pinwidth << " bits (IEEE 1800-2023 28.3.6)");
6813 : : }
6814 : : // Eliminating so pass down all of vup
6815 : : userIterateAndNext(nodep->exprp(), m_vup);
6816 : : nodep->replaceWith(nodep->exprp()->unlinkFrBack());
6817 : : }
6818 : : VL_DO_DANGLING(pushDeletep(nodep), nodep);
6819 : : }
6820 : : }
6821 : : void visit(AstNodeFTask* nodep) override {
6822 : : // Grab width from the output variable (if it's a function)
6823 : : if (nodep->didWidth()) return;
6824 : : if (nodep->doingWidth()) {
6825 : : UINFO(5, "Recursive function or task call: " << nodep);
6826 : : nodep->recursive(true);
6827 : : nodep->didWidth(true);
6828 : : return;
6829 : : }
6830 : : if (nodep->classMethod() && nodep->name() == "rand_mode") {
6831 : : nodep->v3error("The 'rand_mode' method is built-in and cannot be overridden"
6832 : : " (IEEE 1800-2023 18.8)");
6833 : : } else if (nodep->classMethod() && nodep->name() == "constraint_mode") {
6834 : : nodep->v3error("The 'constraint_mode' method is built-in and cannot be overridden"
6835 : : " (IEEE 1800-2023 18.9)");
6836 : : }
6837 : : if (nodep->classMethod() && nodep->name() == "new") {
6838 : : if (nodep->isVirtual())
6839 : : nodep->v3error("class 'new()' cannot be virual (IEEE 1800-2023 18.3)");
6840 : : if (nodep->isStatic())
6841 : : nodep->v3error("class 'new()' cannot be static (IEEE 1800-2023 18.3)");
6842 : : AstNode* firstp = nullptr;
6843 : : for (AstNode* itemp = nodep->stmtsp(); itemp; itemp = itemp->nextp()) {
6844 : : if (AstStmtExpr* const sep = VN_CAST(itemp, StmtExpr)) {
6845 : : if (AstNew* const newp = VN_CAST(sep->exprp(), New)) {
6846 : : if (firstp) {
6847 : : UINFOTREE(1, firstp, "", "-earlier");
6848 : : newp->v3warn(SUPERNFIRST,
6849 : : "'super.new' must be first statement in a 'function "
6850 : : "new' (IEEE 1800-2023 8.15)\n"
6851 : : << newp->warnContextPrimary() << '\n'
6852 : : << firstp->warnOther()
6853 : : << "... Location of earlier statement\n"
6854 : : << firstp->warnContextSecondary());
6855 : : break;
6856 : : }
6857 : : }
6858 : : continue;
6859 : : }
6860 : : if (firstNewStatementOkRecurse(itemp)) continue;
6861 : : firstp = itemp;
6862 : : }
6863 : : }
6864 : : // Function hasn't been widthed, so make it so.
6865 : : // Would use user1 etc, but V3Width called from too many places to spend a user
6866 : : nodep->doingWidth(true);
6867 : : VL_RESTORER(m_funcp);
6868 : : VL_RESTORER(m_ftaskp);
6869 : : m_ftaskp = nodep;
6870 : : // First width the function variable, as if is a recursive function we need data type
6871 : : if (nodep->fvarp()) userIterate(nodep->fvarp(), nullptr);
6872 : : if (nodep->isConstructor()) {
6873 : : // Pretend it's void so less special casing needed when look at dtypes
6874 : : nodep->dtypeSetVoid();
6875 : : } else if (nodep->fvarp()) {
6876 : : m_funcp = VN_AS(nodep, Func);
6877 : : UASSERT_OBJ(m_funcp, nodep, "FTask with function variable, but isn't a function");
6878 : : nodep->dtypeFrom(nodep->fvarp()); // Which will get it from fvarp()->dtypep()
6879 : : } else if (VN_IS(nodep, Property)) {
6880 : : nodep->dtypeSetBit();
6881 : : }
6882 : : WidthVP* vup = nullptr;
6883 : : if (VN_IS(nodep, Property)) vup = WidthVP{SELF, BOTH}.p();
6884 : : userIterateChildren(nodep, vup);
6885 : :
6886 : : nodep->didWidth(true);
6887 : : nodep->doingWidth(false);
6888 : : if (nodep->dpiImport() && !nodep->dpiOpenParent() && markHasOpenArray(nodep)) {
6889 : : nodep->dpiOpenParentInc(); // Mark so V3Task will wait for a child to build calling
6890 : : // func
6891 : : }
6892 : : if (nodep->fvarp() && !nodep->dpiImport() && !nodep->pureVirtual()
6893 : : && (!nodep->stmtsp() || !nodep->stmtsp()->existsAndNext([&](const AstVarRef* varrefp) {
6894 : : return varrefp->varp() == nodep->fvarp();
6895 : : }))) {
6896 : : nodep->v3warn(NORETURN,
6897 : : "Non-void function " << nodep->prettyNameQ() << " has no return value");
6898 : : }
6899 : :
6900 : : std::vector<AstVar*> ports;
6901 : : std::vector<AstAttrOf*> protos;
6902 : : AstAttrOf* protop = nullptr; // Base prototype
6903 : : for (AstNode* stmtp = nodep->stmtsp(); stmtp; stmtp = stmtp->nextp()) {
6904 : : if (AstVar* const portp = VN_CAST(stmtp, Var)) {
6905 : : if (portp->isIO() && !portp->isFuncReturn()) { ports.emplace_back(portp); }
6906 : : } else if (AstAttrOf* const attrp = VN_CAST(stmtp, AttrOf)) {
6907 : : if (attrp->attrType() == VAttrType::FUNC_ARG_PROTO) { protos.emplace_back(attrp); }
6908 : : if (attrp->attrType() == VAttrType::FUNC_RETURN_PROTO) { protop = attrp; }
6909 : : }
6910 : : }
6911 : : if (protop) {
6912 : : for (size_t i = 0; i < std::max(ports.size(), protos.size()); ++i) {
6913 : : if (i >= ports.size() || i >= protos.size()) {
6914 : : protop->v3warn(
6915 : : PROTOTYPEMIS,
6916 : : "In prototype for "
6917 : : << nodep->prettyNameQ()
6918 : : << ", the argument counts do not match the out-of-block declaration"
6919 : : << " (IEEE 1800-2023 8.24)\n"
6920 : : << protop->warnContextPrimary() << '\n'
6921 : : << nodep->warnOther() << "... Location of out-of-block declaration\n"
6922 : : << nodep->warnContextSecondary());
6923 : : break;
6924 : : } else {
6925 : : AstVar* const portp = ports[i];
6926 : : AstAttrOf* const rprotop = protos[i];
6927 : : AstNodeDType* const declDtp = portp->dtypep();
6928 : : AstNodeDType* const protoDtp = rprotop->fromp()->dtypep();
6929 : : if (portp->name() != rprotop->name()) {
6930 : : protoDtp->v3warn(PROTOTYPEMIS,
6931 : : "In prototype for "
6932 : : << nodep->prettyNameQ() << ", argument " << (i + 1)
6933 : : << " named " << rprotop->prettyNameQ()
6934 : : << " mismatches out-of-block argument name "
6935 : : << portp->prettyNameQ() << " (IEEE 1800-2023 8.24)\n"
6936 : : << protoDtp->warnContextPrimary() << '\n'
6937 : : << declDtp->warnOther()
6938 : : << "... Location of out-of-block declaration\n"
6939 : : << declDtp->warnContextSecondary());
6940 : : } else if (!similarDTypeRecurse(protoDtp, declDtp)) {
6941 : : protoDtp->v3warn(
6942 : : PROTOTYPEMIS,
6943 : : "In prototype for "
6944 : : << nodep->prettyNameQ() << ", argument " << portp->prettyNameQ()
6945 : : << " data-type does not match out-of-block"
6946 : : " declaration's data-type (IEEE 1800-2023 8.24)\n"
6947 : : << protoDtp->warnMore() << "... Prototype data type: "
6948 : : << protoDtp->prettyDTypeNameQ() << '\n'
6949 : : << protoDtp->warnMore() << "... Declaration data type: "
6950 : : << declDtp->prettyDTypeNameQ() << '\n'
6951 : : << protoDtp->warnContextPrimary() << '\n'
6952 : : << declDtp->warnOther()
6953 : : << "... Location of out-of-block declaration\n"
6954 : : << declDtp->warnContextSecondary());
6955 : : }
6956 : : }
6957 : : }
6958 : : // V3WidthCommit::visit(AstAttrOf) will delete nodep
6959 : : }
6960 : : }
6961 : : void visit(AstConstraint* nodep) override {
6962 : : if (nodep->didWidth()) return;
6963 : : VL_RESTORER(m_constraintp);
6964 : : m_constraintp = nodep;
6965 : : userIterateChildren(nodep, nullptr);
6966 : : nodep->didWidth(true);
6967 : : }
6968 : : void visit(AstProperty* nodep) override {
6969 : : if (nodep->didWidth()) return;
6970 : : if (nodep->doingWidth()) {
6971 : : UINFO(5, "Recursive property call: " << nodep);
6972 : : nodep->v3warn(E_UNSUPPORTED,
6973 : : "Unsupported: Recursive property call: " << nodep->prettyNameQ());
6974 : : nodep->recursive(true);
6975 : : nodep->didWidth(true);
6976 : : return;
6977 : : }
6978 : : nodep->doingWidth(true);
6979 : : VL_RESTORER(m_ftaskp);
6980 : : m_ftaskp = nodep;
6981 : : // Property call will be replaced by property body in V3AssertPre. Property body has bit
6982 : : // dtype, so set it here too
6983 : : nodep->dtypeSetBit();
6984 : : for (AstNode* propStmtp = nodep->stmtsp(); propStmtp; propStmtp = propStmtp->nextp()) {
6985 : : if (VN_IS(propStmtp, Var)) {
6986 : : userIterate(propStmtp, nullptr);
6987 : : } else if (VN_IS(propStmtp, PropSpec)) {
6988 : : iterateCheckSelf(nodep, "PropSpec", propStmtp, SELF, BOTH);
6989 : : } else if (VN_IS(propStmtp, InitialStaticStmt)
6990 : : || VN_IS(propStmtp, InitialAutomaticStmt)) {
6991 : : // Property-local variable initialization -- iterate this node only
6992 : : userIterate(propStmtp, nullptr);
6993 : : } else {
6994 : : propStmtp->v3fatalSrc("Invalid statement under AstProperty");
6995 : : }
6996 : : }
6997 : : nodep->didWidth(true);
6998 : : nodep->doingWidth(false);
6999 : : }
7000 : : void visit(AstReturn* nodep) override { nodep->v3fatalSrc("'return' missed in LinkJump"); }
7001 : : void visit(AstSequence* nodep) override {
7002 : : // IEEE 1800-2023 16.7 (sequence declarations), 16.8 (sequence instances)
7003 : : // Width-check the sequence body so expressions are typed for later inlining.
7004 : : // Referenced sequences in assertion context will be inlined by V3AssertPre.
7005 : : if (nodep->didWidth()) return;
7006 : : nodep->dtypeSetBit();
7007 : : for (AstNode* stmtp = nodep->stmtsp(); stmtp; stmtp = stmtp->nextp()) {
7008 : : if (VN_IS(stmtp, Var)) {
7009 : : userIterate(stmtp, nullptr);
7010 : : } else if (VN_IS(stmtp, NodeExpr)) {
7011 : : iterateCheckSelf(nodep, "SeqBody", stmtp, SELF, BOTH);
7012 : : } else {
7013 : : stmtp->v3fatalSrc("Invalid statement under AstSequence");
7014 : : }
7015 : : }
7016 : : nodep->didWidth(true);
7017 : : // Keep all sequences for now; cleanup happens after V3AssertPre inlining.
7018 : : }
7019 : :
7020 : : AstPackage* getItemPackage(AstNode* pkgItemp) {
7021 : : while (pkgItemp->backp() && pkgItemp->backp()->nextp() == pkgItemp) {
7022 : : pkgItemp = pkgItemp->backp();
7023 : : }
7024 : : return VN_CAST(pkgItemp->backp(), Package);
7025 : : }
7026 : : const AstClass* containingClass(AstNode* nodep) {
7027 : : // abovep is still needed, m_containingClassp is just a cache
7028 : : if (const AstClass* const classp = VN_CAST(nodep, Class))
7029 : : return m_containingClassp[nodep] = classp;
7030 : : if (const AstClassPackage* const packagep = VN_CAST(nodep, ClassPackage)) {
7031 : : return m_containingClassp[nodep] = packagep->classp();
7032 : : }
7033 : : if (m_containingClassp.find(nodep) != m_containingClassp.end()) {
7034 : : return m_containingClassp[nodep];
7035 : : }
7036 : : if (AstNode* const abovep = nodep->aboveLoopp()) {
7037 : : return m_containingClassp[nodep] = containingClass(abovep);
7038 : : } else {
7039 : : return m_containingClassp[nodep] = nullptr;
7040 : : }
7041 : : }
7042 : : void visit(AstFuncRef* nodep) override {
7043 : : visit(static_cast<AstNodeFTaskRef*>(nodep));
7044 : : if (nodep->taskp() && VN_IS(nodep->taskp(), Task)) {
7045 : : UASSERT_OBJ(m_vup, nodep, "Function reference where widthed expression expectation");
7046 : : if (m_vup->prelim() && !VN_IS(nodep->backp(), StmtExpr))
7047 : : nodep->v3error(
7048 : : "Cannot call a task/void-function as a function: " << nodep->prettyNameQ());
7049 : : nodep->dtypeSetVoid();
7050 : : } else {
7051 : : nodep->dtypeFrom(nodep->taskp());
7052 : : }
7053 : : if (nodep->fileline()->timingOn()) {
7054 : : AstNodeModule* const classp = nodep->classOrPackagep();
7055 : : if (nodep->name() == "self" && classp->name() == "process") {
7056 : : // Find if package the class is in is std::
7057 : : AstPackage* const packagep = getItemPackage(classp);
7058 : : if (packagep && packagep->name() == "std") {
7059 : : methodCallWarnTiming(nodep, "process");
7060 : : }
7061 : : }
7062 : : }
7063 : : // UINFOTREE(1, nodep, "", "FuncOut");
7064 : : }
7065 : : // Returns true if dtypep0 and dtypep1 have same dimensions
7066 : : static bool areSameSize(AstUnpackArrayDType* dtypep0, AstUnpackArrayDType* dtypep1) {
7067 : : const std::vector<AstUnpackArrayDType*> dims0 = dtypep0->unpackDimensions();
7068 : : const std::vector<AstUnpackArrayDType*> dims1 = dtypep1->unpackDimensions();
7069 : : if (dims0.size() != dims1.size()) return false;
7070 : : for (size_t i = 0; i < dims0.size(); ++i) {
7071 : : if (dims0[i]->elementsConst() != dims1[i]->elementsConst()) return false;
7072 : : }
7073 : : return true;
7074 : : }
7075 : : // Makes sure that port and pin have same size and same datatype
7076 : : void checkUnpackedArrayArgs(AstVar* portp, AstNode* pinp) {
7077 : : if (AstUnpackArrayDType* const portDtypep
7078 : : = VN_CAST(portp->dtypep()->skipRefp(), UnpackArrayDType)) {
7079 : : if (AstUnpackArrayDType* const pinDtypep
7080 : : = VN_CAST(pinp->dtypep()->skipRefp(), UnpackArrayDType)) {
7081 : : if (!areSameSize(portDtypep, pinDtypep)) {
7082 : : pinp->v3warn(E_UNSUPPORTED,
7083 : : "Shape of the argument does not match the shape of the parameter "
7084 : : << "(" << pinDtypep->prettyDTypeNameQ() << " v.s. "
7085 : : << portDtypep->prettyDTypeNameQ() << ")");
7086 : : }
7087 : : if (portDtypep->basicp()->width() != pinDtypep->basicp()->width()
7088 : : || (portDtypep->basicp()->keyword() != pinDtypep->basicp()->keyword()
7089 : : && !(portDtypep->basicp()->keyword() == VBasicDTypeKwd::LOGIC_IMPLICIT
7090 : : && pinDtypep->basicp()->keyword() == VBasicDTypeKwd::LOGIC)
7091 : : && !(portDtypep->basicp()->keyword() == VBasicDTypeKwd::LOGIC
7092 : : && pinDtypep->basicp()->keyword()
7093 : : == VBasicDTypeKwd::LOGIC_IMPLICIT))) {
7094 : : pinp->v3warn(E_UNSUPPORTED,
7095 : : "Shape of the argument does not match the shape of the parameter "
7096 : : << "(" << pinDtypep->basicp()->prettyDTypeNameQ() << " v.s. "
7097 : : << portDtypep->basicp()->prettyDTypeNameQ() << ")");
7098 : : }
7099 : : } else {
7100 : : pinp->v3warn(E_UNSUPPORTED, "Argument is not an unpacked array while parameter "
7101 : : << portp->prettyNameQ() << " is");
7102 : : }
7103 : : }
7104 : : }
7105 : : void processFTaskRefArgs(AstNodeFTaskRef* nodep) {
7106 : : // For arguments, is assignment-like context; see IEEE rules in AstNodeAssign
7107 : : // Function hasn't been widthed, so make it so.
7108 : : UINFO(5, " FTASKREF " << nodep);
7109 : : UASSERT_OBJ(nodep->taskp(), nodep, "Unlinked");
7110 : : if (nodep->didWidth()) return;
7111 : : userIterate(nodep->taskp(), nullptr);
7112 : : //
7113 : : // And do the arguments to the task/function too
7114 : : do {
7115 : : reloop:
7116 : : // taskConnects may create a new task, and change nodep->taskp()
7117 : : const V3TaskConnects tconnects
7118 : : = V3Task::taskConnects(nodep, nodep->taskp()->stmtsp(), &m_taskConnectState);
7119 : : if (m_taskConnectState.didWrap()) m_memberMap.clear(); // As added a member
7120 : : for (const auto& tconnect : tconnects) {
7121 : : const AstVar* const portp = tconnect.first;
7122 : : AstArg* const argp = tconnect.second;
7123 : : AstNodeExpr* pinp = argp->exprp();
7124 : : if (!pinp) continue; // Argument error we'll find later
7125 : : // Prelim may cause the node to get replaced; we've lost our
7126 : : // pointer, so need to iterate separately later
7127 : : if (portp->attrSFormat()
7128 : : && (!VN_IS(pinp, SFormatF) || pinp->nextp())) { // Not already done
7129 : : UINFO(4, " sformat via metacomment: " << nodep);
7130 : : VNRelinker handle;
7131 : : argp->unlinkFrBackWithNext(&handle); // Format + additional args, if any
7132 : : AstNodeExpr* argsp = nullptr;
7133 : : while (AstArg* const nextargp = VN_AS(argp->nextp(), Arg)) {
7134 : : // Expression goes to SFormatF
7135 : : argsp = argsp->addNext(nextargp->exprp()->unlinkFrBackWithNext());
7136 : : nextargp->unlinkFrBack()->deleteTree(); // Remove the call's Arg wrapper
7137 : : }
7138 : : string format;
7139 : : if (VN_IS(pinp, Const)) {
7140 : : format = VN_AS(pinp, Const)->num().toString();
7141 : : } else {
7142 : : pinp->v3error(
7143 : : "Format to $display-like function must have constant format string");
7144 : : }
7145 : : VL_DO_DANGLING(pushDeletep(argp), argp);
7146 : : AstSFormatF* const newp
7147 : : = new AstSFormatF{nodep->fileline(), format, false, argsp};
7148 : : if (!newp->scopeNamep() && newp->formatScopeTracking()) {
7149 : : newp->scopeNamep(new AstScopeName{newp->fileline(), true});
7150 : : }
7151 : : handle.relink(new AstArg{newp->fileline(), "", newp});
7152 : : // Connection list is now incorrect (has extra args in it).
7153 : : goto reloop; // so exit early; next loop will correct it
7154 : : } //
7155 : : else if (portp->basicp() && portp->basicp()->keyword() == VBasicDTypeKwd::STRING
7156 : : && !VN_IS(pinp, CvtPackString)
7157 : : && !VN_IS(pinp, SFormatF) // Already generates a string
7158 : : && !VN_IS(portp->dtypep(), UnpackArrayDType) // Unpacked array must match
7159 : : && !(VN_IS(pinp, VarRef)
7160 : : && VN_AS(pinp, VarRef)->varp()->basicp()->keyword()
7161 : : == VBasicDTypeKwd::STRING)) {
7162 : : UINFO(4, " Add CvtPackString: " << pinp);
7163 : : VNRelinker handle;
7164 : : pinp->unlinkFrBack(&handle); // No next, that's the next pin
7165 : : AstNodeExpr* const newp = new AstCvtPackString{pinp->fileline(), pinp};
7166 : : handle.relink(newp);
7167 : : pinp = newp;
7168 : : }
7169 : : // AstPattern requires assignments to pass datatype on PRELIM
7170 : : VL_DO_DANGLING(userIterate(pinp, WidthVP{portp->dtypep(), PRELIM}.p()), pinp);
7171 : : }
7172 : : } while (false);
7173 : : // Stage 2
7174 : : {
7175 : : const V3TaskConnects tconnects = V3Task::taskConnects(nodep, nodep->taskp()->stmtsp());
7176 : : for (const auto& tconnect : tconnects) {
7177 : : AstVar* const portp = tconnect.first;
7178 : : const AstArg* const argp = tconnect.second;
7179 : : AstNodeExpr* const pinp = argp->exprp();
7180 : : if (!pinp) continue; // Argument error we'll find later
7181 : : // Change data types based on above accept completion
7182 : : if (nodep->taskp()->dpiImport()) checkUnpackedArrayArgs(portp, pinp);
7183 : : if (portp->isDouble()) VL_DO_DANGLING(spliceCvtD(pinp), pinp);
7184 : : }
7185 : : }
7186 : : // Stage 3
7187 : : {
7188 : : const V3TaskConnects tconnects = V3Task::taskConnects(nodep, nodep->taskp()->stmtsp());
7189 : : for (const auto& tconnect : tconnects) {
7190 : : const AstVar* const portp = tconnect.first;
7191 : : const AstArg* const argp = tconnect.second;
7192 : : AstNodeExpr* const pinp = argp->exprp();
7193 : : if (!pinp) continue; // Argument error we'll find later
7194 : : // Do PRELIM again, because above accept may have exited early
7195 : : // due to node replacement
7196 : : userIterate(pinp, WidthVP{portp->dtypep(), PRELIM}.p());
7197 : : }
7198 : : }
7199 : : // Cleanup any open arrays
7200 : : if (markHasOpenArray(nodep->taskp())) makeOpenArrayShell(nodep);
7201 : : // Stage 4
7202 : : {
7203 : : const V3TaskConnects tconnects = V3Task::taskConnects(nodep, nodep->taskp()->stmtsp());
7204 : : for (const auto& tconnect : tconnects) {
7205 : : const AstVar* const portp = tconnect.first;
7206 : : const AstArg* const argp = tconnect.second;
7207 : : AstNodeExpr* const pinp = argp->exprp();
7208 : : if (!pinp) continue; // Argument error we'll find later
7209 : : AstNodeDType* const portDTypep = portp->dtypep()->skipRefToEnump();
7210 : : const AstNodeDType* const pinDTypep = pinp->dtypep()->skipRefToEnump();
7211 : : if (portp->direction() == VDirection::REF
7212 : : && !similarDTypeRecurse(portDTypep, pinDTypep)) {
7213 : : pinp->v3error("Ref argument requires matching types;"
7214 : : << " port " << portp->prettyNameQ() << " requires "
7215 : : << portDTypep->prettyDTypeNameQ() << " but connection is "
7216 : : << pinDTypep->prettyDTypeNameQ() << ".");
7217 : : } else if (portp->isWritable() && pinp->width() != portp->width()) {
7218 : : pinp->v3widthWarn(portp->width(), pinp->width(),
7219 : : "Function output argument "
7220 : : << portp->prettyNameQ() << " requires " << portp->width()
7221 : : << " bits, but connection's " << pinp->prettyTypeName()
7222 : : << " generates " << pinp->width() << " bits.");
7223 : : VNRelinker relinkHandle;
7224 : : pinp->unlinkFrBack(&relinkHandle);
7225 : : AstNodeExpr* const newp = new AstResizeLValue{pinp->fileline(), pinp};
7226 : : relinkHandle.relink(newp);
7227 : : }
7228 : : if (portp->isWritable()) V3LinkLValue::linkLValueSet(pinp);
7229 : : if (!portp->basicp() || portp->basicp()->isOpaque()) {
7230 : : // Output args: at return caller = callee, reverse direction.
7231 : : checkClassAssign(nodep, "Function Argument", pinp, portDTypep,
7232 : : portp->direction() == VDirection::OUTPUT);
7233 : : userIterate(pinp, WidthVP{portDTypep, FINAL}.p());
7234 : : } else {
7235 : : iterateCheckAssign(nodep, "Function Argument", pinp, FINAL, portDTypep);
7236 : : }
7237 : : }
7238 : : }
7239 : : }
7240 : :
7241 : : void handleStdRandomizeArgs(AstNodeFTaskRef* const nodep) {
7242 : : AstConst* nullp = nullptr;
7243 : : for (AstArg *argp = nodep->argsp(), *nextp; argp; argp = nextp) {
7244 : : nextp = VN_AS(argp->nextp(), Arg);
7245 : : AstNodeExpr* const exprp = argp->exprp();
7246 : : if (AstConst* const constp = VN_CAST(exprp, Const)) {
7247 : : if (constp->num().isNull()) {
7248 : : nullp = constp;
7249 : : continue;
7250 : : }
7251 : : }
7252 : : if (VN_IS(exprp, MemberSel)) {
7253 : : // Non-standard usage: std::randomize() with class-scoped member
7254 : : // IEEE 1800-2023 (18.12) limits args to current scope variables.
7255 : : // Verilator accepts this for compatibility with other simulators.
7256 : : continue;
7257 : : }
7258 : : if (VN_IS(exprp, VarRef) || VN_IS(exprp, ArraySel) || VN_IS(exprp, StructSel)) {
7259 : : // Valid usage
7260 : : continue;
7261 : : }
7262 : : if (const AstCMethodHard* const methodp = VN_CAST(exprp, CMethodHard)) {
7263 : : if (methodp->method() == VCMethod::ARRAY_AT
7264 : : || methodp->method() == VCMethod::ARRAY_AT_WRITE) {
7265 : : continue;
7266 : : }
7267 : : }
7268 : : argp->v3error("Non-variable arguments for 'std::randomize()'.");
7269 : : }
7270 : : if (nullp) nullp->v3error("'std::randomize()' does not accept 'null' as arguments.");
7271 : : }
7272 : : void visit(AstNodeFTaskRef* nodep) override {
7273 : : // For arguments, is assignment-like context; see IEEE rules in AstNodeAssign
7274 : : // Function hasn't been widthed, so make it so.
7275 : : if (nodep->didWidth()) return;
7276 : : UINFO(5, " FTASKREF " << nodep);
7277 : : AstWith* withp = nullptr;
7278 : : if (nodep->name() == "rand_mode" || nodep->name() == "constraint_mode") {
7279 : : v3Global.useRandomizeMethods(true);
7280 : : nodep->dtypep(nodep->findBasicDType(VBasicDTypeKwd::INT));
7281 : : return; // Handled in V3Randomize
7282 : : } else if (nodep->name() == "randomize" || nodep->name() == "srandom"
7283 : : || (!nodep->taskp()
7284 : : && (nodep->name() == "get_randstate"
7285 : : || nodep->name() == "set_randstate"))) {
7286 : : // TODO perhaps this should move to V3LinkDot
7287 : : AstClass* const classp = VN_CAST(nodep->classOrPackagep(), Class);
7288 : : if (nodep->classOrPackagep() && nodep->classOrPackagep()->name() == "std") {
7289 : : v3Global.useRandomizeMethods(true);
7290 : : AstNodeDType* const adtypep = nodep->findBitDType();
7291 : : withp = methodWithClause(nodep, false, false, adtypep->findVoidDType(),
7292 : : adtypep->findBitDType(), adtypep);
7293 : : for (const AstArg* argp = nodep->argsp(); argp; argp = VN_AS(argp->nextp(), Arg)) {
7294 : : userIterateAndNext(argp->exprp(), WidthVP{SELF, BOTH}.p());
7295 : : }
7296 : : handleStdRandomizeArgs(nodep); // Provided args should be in current scope
7297 : : processFTaskRefArgs(nodep);
7298 : : if (withp) nodep->withp(withp);
7299 : : nodep->didWidth(true);
7300 : : return;
7301 : : }
7302 : : UASSERT_OBJ(classp, nodep, "Classless rand-thing should have failed in V3LinkDot");
7303 : : if (nodep->name() == "randomize") {
7304 : : AstClassRefDType* const adtypep
7305 : : = new AstClassRefDType{nodep->fileline(), classp, nullptr};
7306 : : v3Global.rootp()->typeTablep()->addTypesp(adtypep);
7307 : : withp = methodWithClause(nodep, false, false, adtypep->findVoidDType(),
7308 : : adtypep->findBitDType(), adtypep);
7309 : : for (const AstArg* argp = nodep->argsp(); argp; argp = VN_AS(argp->nextp(), Arg)) {
7310 : : userIterateAndNext(argp->exprp(), WidthVP{SELF, BOTH}.p());
7311 : : }
7312 : : handleRandomizeArgs(nodep, classp);
7313 : : } else if (nodep->name() == "srandom") {
7314 : : nodep->taskp(V3Randomize::newSRandomFunc(m_memberMap, classp));
7315 : : m_memberMap.clear();
7316 : : } else if (nodep->name() == "get_randstate") {
7317 : : methodOkArguments(nodep, 0, 0);
7318 : : classp->baseMostClassp()->needRNG(true);
7319 : : v3Global.useRandomizeMethods(true);
7320 : : AstCExpr* const newp
7321 : : = new AstCExpr{nodep->fileline(), "__Vm_rng.get_randstate()", 1};
7322 : : newp->dtypeSetString();
7323 : : nodep->replaceWith(newp);
7324 : : VL_DO_DANGLING(pushDeletep(nodep), nodep);
7325 : : return;
7326 : : } else if (nodep->name() == "set_randstate") {
7327 : : methodOkArguments(nodep, 1, 1);
7328 : : AstNodeExpr* const expr1p = nodep->argsp()->exprp(); // May edit
7329 : : iterateCheckString(nodep, "LHS", expr1p, BOTH);
7330 : : AstNodeExpr* const exprp = nodep->argsp()->exprp();
7331 : : classp->baseMostClassp()->needRNG(true);
7332 : : v3Global.useRandomizeMethods(true);
7333 : : AstCExpr* const newp
7334 : : = new AstCExpr{nodep->fileline(), "__Vm_rng.set_randstate(", 1};
7335 : : newp->add(exprp->unlinkFrBack());
7336 : : newp->add(")");
7337 : : newp->dtypeSetVoid();
7338 : : nodep->replaceWith(newp);
7339 : : VL_DO_DANGLING(pushDeletep(nodep), nodep);
7340 : : return;
7341 : : } else {
7342 : : UASSERT_OBJ(false, nodep, "Bad case");
7343 : : }
7344 : : }
7345 : : UASSERT_OBJ(nodep->taskp(), nodep, "Unlinked");
7346 : : if (nodep->didWidth()) {
7347 : : if (withp) nodep->withp(withp);
7348 : : return;
7349 : : }
7350 : : if ((nodep->taskp()->classMethod() && !nodep->taskp()->isStatic())
7351 : : && !VN_IS(m_procedurep, InitialAutomatic) && !m_constraintp) {
7352 : : bool allow = false;
7353 : : if (m_ftaskp && m_ftaskp->classMethod() && !m_ftaskp->isStatic()) {
7354 : : if (const AstFuncRef* const funcRefp = VN_CAST(nodep, FuncRef)) {
7355 : : allow = funcRefp->superReference();
7356 : : } else if (const AstTaskRef* const taskRefp = VN_CAST(nodep, TaskRef)) {
7357 : : allow = taskRefp->superReference();
7358 : : }
7359 : : if (!allow) {
7360 : : const AstClass* callerClassp = containingClass(m_ftaskp);
7361 : : if (!callerClassp) callerClassp = containingClass(m_ftaskp->classOrPackagep());
7362 : : const AstClass* calleeClassp = VN_CAST(nodep->classOrPackagep(), Class);
7363 : : if (!calleeClassp) calleeClassp = containingClass(nodep->taskp());
7364 : : allow = AstClass::isClassExtendedFrom(callerClassp, calleeClassp);
7365 : : }
7366 : : }
7367 : : if (!allow) {
7368 : : nodep->v3error("Cannot call non-static member function "
7369 : : << nodep->prettyNameQ() << " without object (IEEE 1800-2023 8.10)");
7370 : : }
7371 : : }
7372 : : if (nodep->taskp() && !nodep->scopeNamep()
7373 : : && (nodep->taskp()->dpiContext() || nodep->taskp()->dpiExport())) {
7374 : : nodep->scopeNamep(new AstScopeName{nodep->fileline(), false});
7375 : : }
7376 : : // And do the arguments to the task/function too
7377 : : processFTaskRefArgs(nodep);
7378 : : if (withp) nodep->withp(withp);
7379 : : nodep->didWidth(true);
7380 : : // See steps that follow in visit(AstFuncRef*)
7381 : : }
7382 : : void visit(AstNodeProcedure* nodep) override {
7383 : : assertAtStatement(nodep);
7384 : : VL_RESTORER(m_procedurep);
7385 : : m_procedurep = nodep;
7386 : : userIterateChildren(nodep, nullptr);
7387 : : }
7388 : : void visit(AstSenItem* nodep) override {
7389 : : if (nodep->isComboStar()) return;
7390 : : UASSERT_OBJ(nodep->isClocked(), nodep, "Invalid edge");
7391 : : // Optimize concat/replicate senitems; this has to be done here at the latest, otherwise we
7392 : : // emit WIDTHCONCAT if there are unsized constants
7393 : : if (VN_IS(nodep->sensp(), Concat) || VN_IS(nodep->sensp(), Replicate)) {
7394 : : auto* const concatOrReplp = VN_CAST(nodep->sensp(), NodeBiop);
7395 : : auto* const rhsp = concatOrReplp->rhsp()->unlinkFrBack();
7396 : : if (nodep->edgeType() == VEdgeType::ET_CHANGED) {
7397 : : // If it's ET_CHANGED, split concatenations into multiple senitems
7398 : : auto* const lhsp = concatOrReplp->lhsp()->unlinkFrBack();
7399 : : nodep->addNextHere(new AstSenItem{lhsp->fileline(), nodep->edgeType(), lhsp});
7400 : : } // Else only use the RHS
7401 : : nodep->replaceWith(new AstSenItem{rhsp->fileline(), nodep->edgeType(), rhsp});
7402 : : VL_DO_DANGLING(nodep->deleteTree(), nodep);
7403 : : } else {
7404 : : userIterateChildren(nodep, WidthVP{SELF, BOTH}.p());
7405 : : if (nodep->edgeType().anEdge()) {
7406 : : AstNodeDType* const sensDtp = nodep->sensp()->dtypep()->skipRefp();
7407 : : if (sensDtp->isDouble()) {
7408 : : nodep->sensp()->v3error(
7409 : : "Edge event control not legal on real type (IEEE 1800-2023 6.12.1)");
7410 : : } else if (sensDtp->basicp() && !sensDtp->basicp()->keyword().isIntNumeric()) {
7411 : : nodep->sensp()->v3error("Edge event control not legal on non-integral type "
7412 : : "(IEEE 1800-2023 9.4.2)");
7413 : : }
7414 : : }
7415 : : }
7416 : : }
7417 : : void visit(AstClockingItem* nodep) override {
7418 : : nodep->exprp()->foreach([nodep](AstVarRef* const refp) {
7419 : : refp->access(nodep->direction().isWritable() ? VAccess::WRITE : VAccess::READ);
7420 : : });
7421 : : userIterateChildren(nodep, WidthVP{SELF, PRELIM}.p());
7422 : : }
7423 : : void visit(AstWait* nodep) override {
7424 : : if (!m_underFork && VN_IS(m_ftaskp, Func)) {
7425 : : nodep->v3error("Wait statements are not legal in functions. Suggest use a task "
7426 : : "(IEEE 1800-2023 13.4.4)");
7427 : : VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
7428 : : return;
7429 : : }
7430 : : if (nodep->fileline()->timingOn()) {
7431 : : if (v3Global.opt.timing().isSetTrue()) {
7432 : : iterateCheckBool(nodep, "Wait", nodep->condp(),
7433 : : BOTH); // it's like an if() condition.
7434 : : // TODO check also inside complex event expressions
7435 : : if (AstNodeVarRef* const varrefp = VN_CAST(nodep->condp(), NodeVarRef)) {
7436 : : if (varrefp->isEvent()) {
7437 : : varrefp->v3error("Wait statement conditions do not take raw events"
7438 : : " (IEEE 1800-2023 15.5.3)\n"
7439 : : << varrefp->warnMore() << "... Suggest use '"
7440 : : << varrefp->prettyName() << ".triggered'");
7441 : : }
7442 : : }
7443 : : iterateNull(nodep->stmtsp());
7444 : : return;
7445 : : } else if (v3Global.opt.timing().isSetFalse()) {
7446 : : nodep->v3warn(E_NOTIMING, "Wait statements require --timing");
7447 : : } else {
7448 : : nodep->v3warn(E_NEEDTIMINGOPT, "Use --timing or --no-timing to specify how wait "
7449 : : "statements should be handled");
7450 : : }
7451 : : }
7452 : : // If we ignore timing:
7453 : : // Statements we'll just execute immediately; equivalent to if they followed this
7454 : : if (AstNode* const stmtsp = nodep->stmtsp()) {
7455 : : stmtsp->unlinkFrBackWithNext();
7456 : : nodep->replaceWith(stmtsp);
7457 : : } else {
7458 : : nodep->unlinkFrBack();
7459 : : }
7460 : : VL_DO_DANGLING(nodep->deleteTree(), nodep);
7461 : : }
7462 : : void visit(AstWith* nodep) override {
7463 : : // Should otherwise be underneath a method call
7464 : : AstNodeDType* const vdtypep = m_vup->dtypeNullSkipRefp();
7465 : : VL_RESTORER(m_withp);
7466 : : m_withp = nodep;
7467 : : userIterateChildren(nodep->indexArgRefp(), nullptr);
7468 : : userIterateChildren(nodep->valueArgRefp(), nullptr);
7469 : : if (!nodep->exprp()) {
7470 : : nodep->dtypeSetVoid();
7471 : : } else {
7472 : : if (!nodep->exprp()->hasDType()) {
7473 : : userIterateAndNext(nodep->exprp(), nullptr);
7474 : : } else {
7475 : : if (vdtypep) {
7476 : : userIterateAndNext(nodep->exprp(), WidthVP{nodep->dtypep(), PRELIM}.p());
7477 : : } else { // 'sort with' allows arbitrary type
7478 : : userIterateAndNext(nodep->exprp(), WidthVP{SELF, PRELIM}.p());
7479 : : }
7480 : : }
7481 : :
7482 : : if (!nodep->exprp()->hasDType()) {
7483 : : nodep->dtypeSetVoid();
7484 : : } else {
7485 : : nodep->dtypeFrom(nodep->exprp());
7486 : : iterateCheckAssign(nodep, "'with' return value", nodep->exprp(), FINAL,
7487 : : nodep->dtypep());
7488 : : }
7489 : : }
7490 : : }
7491 : : void visit(AstLambdaArgRef* nodep) override {
7492 : : UASSERT_OBJ(m_withp, nodep, "LambdaArgRef not underneath 'with' lambda");
7493 : : if (nodep->index()) {
7494 : : nodep->dtypeFrom(m_withp->indexArgRefp());
7495 : : } else {
7496 : : nodep->dtypeFrom(m_withp->valueArgRefp());
7497 : : }
7498 : : }
7499 : : void visit(AstNetlist* nodep) override {
7500 : : // Iterate modules backwards, in bottom-up order. That's faster
7501 : : userIterateChildrenBackwardsConst(nodep, nullptr);
7502 : : }
7503 : :
7504 : : //--------------------
7505 : : // Default
7506 : : void visit(AstNodeExpr* nodep) override {
7507 : : if (!nodep->didWidth()) {
7508 : : nodep->v3fatalSrc(
7509 : : "Visit function missing? Widthed function missing for math node: " << nodep);
7510 : : }
7511 : : userIterateChildren(nodep, nullptr);
7512 : : }
7513 : : void visit(AstResizeLValue* nodep) override {
7514 : : // RESIZELVALUE adjusts width of lvalues for assignments/function calls
7515 : : // The parent context determines the required width
7516 : : UINFO(9, "visit AstResizeLValue " << nodep << endl);
7517 : : if (nodep->didWidthAndSet()) return;
7518 : :
7519 : : UASSERT_OBJ(nodep->lhsp(), nodep, "ResizeLValue missing lhs expression");
7520 : :
7521 : : // First, process child to know its natural width
7522 : : if (m_vup->prelim()) {
7523 : : userIterateAndNext(nodep->lhsp(), WidthVP{CONTEXT_DET, PRELIM}.p());
7524 : : }
7525 : :
7526 : : // Get the required width from parent context
7527 : : if (AstNodeDType* const vdtypep = m_vup->dtypeNullp()) {
7528 : : // Parent specified required width - use it
7529 : : nodep->dtypeFrom(vdtypep);
7530 : : } else if (!nodep->dtypep()) {
7531 : : // No parent context, use child's type
7532 : : nodep->dtypeFrom(nodep->lhsp());
7533 : : }
7534 : :
7535 : : // Verify we ended up with a valid datatype
7536 : : UASSERT_OBJ(nodep->dtypep(), nodep, "ResizeLValue still missing dtype after visiting");
7537 : : UASSERT_OBJ(nodep->width() > 0, nodep, "ResizeLValue has invalid width");
7538 : :
7539 : : // Log the transformation for debugging
7540 : : UINFO(9, " ResizeLValue: " << nodep->lhsp()->width() << " bits -> " << nodep->width()
7541 : : << " bits" << endl);
7542 : :
7543 : : // Final processing
7544 : : userIterateAndNext(nodep->lhsp(), WidthVP{SELF, FINAL}.p());
7545 : : nodep->didWidth(true);
7546 : : }
7547 : : void visitClass(AstClass* nodep) {
7548 : : if (nodep->didWidthAndSet()) return;
7549 : :
7550 : : // If the class is std::process
7551 : : if (nodep->name() == "process") {
7552 : : AstPackage* const packagep = getItemPackage(nodep);
7553 : : if (packagep && packagep->name() == "std") {
7554 : : // Change type of m_process to VlProcessRef
7555 : : if (AstVar* const varp
7556 : : = VN_CAST(m_memberMap.findMember(nodep, "m_process"), Var)) {
7557 : : AstNodeDType* const dtypep = varp->getChildDTypep();
7558 : : if (!varp->dtypep()) {
7559 : : VL_DO_DANGLING(pushDeletep(dtypep->unlinkFrBack()), dtypep);
7560 : : }
7561 : : AstBasicDType* const newdtypep = new AstBasicDType{
7562 : : nodep->fileline(), VBasicDTypeKwd::PROCESS_REFERENCE, VSigning::UNSIGNED};
7563 : : v3Global.rootp()->typeTablep()->addTypesp(newdtypep);
7564 : : varp->dtypep(newdtypep);
7565 : : }
7566 : : // Mark that self requires process instance
7567 : : if (AstNodeFTask* const ftaskp
7568 : : = VN_CAST(m_memberMap.findMember(nodep, "self"), NodeFTask)) {
7569 : : ftaskp->setNeedProcess();
7570 : : }
7571 : : }
7572 : : }
7573 : :
7574 : : // Must do extends first, as we may in functions under this class
7575 : : // start following a tree of extends that takes us to other classes
7576 : : userIterateAndNext(nodep->extendsp(), nullptr);
7577 : : userIterateChildren(nodep, nullptr); // First size all members
7578 : : }
7579 : : void visit(AstNodeModule* nodep) override {
7580 : : assertAtStatement(nodep);
7581 : : VL_RESTORER(m_insideTempNames);
7582 : : if (AstClass* const classp = VN_CAST(nodep, Class)) {
7583 : : visitClass(classp);
7584 : : } else {
7585 : : VL_RESTORER(m_modep);
7586 : : m_modep = nodep;
7587 : : // Collect local variables for ASSIGNIN hierarchical reference check
7588 : : m_curModVars.clear();
7589 : : nodep->foreach([this](const AstVar* varp) { m_curModVars.insert(varp); });
7590 : : userIterateChildren(nodep, nullptr);
7591 : : }
7592 : : }
7593 : : void visit(AstNode* nodep) override {
7594 : : // Default: Just iterate
7595 : : UASSERT_OBJ(!m_vup, nodep,
7596 : : "Visit function missing? Widthed expectation for this node: " << nodep);
7597 : : userIterateChildren(nodep, nullptr);
7598 : : }
7599 : :
7600 : : //----------------------------------------------------------------------
7601 : : // WIDTH METHODs -- all iterate
7602 : :
7603 : : void visit_Or_Lu64(AstNodeUniop* nodep) {
7604 : : // CALLER: AstBitsToRealD
7605 : : // Real: Output real
7606 : : // LHS presumed self-determined, then coerced to real
7607 : : assertAtExpr(nodep);
7608 : : if (m_vup->prelim()) { // First stage evaluation
7609 : : nodep->dtypeSetDouble();
7610 : : AstNodeDType* const subDTypep = nodep->findLogicDType(64, 64, VSigning::UNSIGNED);
7611 : : // Self-determined operand
7612 : : userIterateAndNext(nodep->lhsp(), WidthVP{SELF, PRELIM}.p());
7613 : : iterateCheck(nodep, "LHS", nodep->lhsp(), SELF, FINAL, subDTypep, EXTEND_EXP);
7614 : : }
7615 : : }
7616 : : void visit(AstIToRD* nodep) override {
7617 : : // Real: Output real
7618 : : // LHS presumed self-determined, then coerced to real
7619 : : assertAtExpr(nodep);
7620 : : if (m_vup->prelim()) { // First stage evaluation
7621 : : nodep->dtypeSetDouble();
7622 : : // Self-determined operand (TODO check if numeric type)
7623 : : userIterateAndNext(nodep->lhsp(), WidthVP{SELF, PRELIM}.p());
7624 : : if (nodep->lhsp()->isSigned()) {
7625 : : nodep->replaceWith(
7626 : : new AstISToRD{nodep->fileline(), nodep->lhsp()->unlinkFrBack()});
7627 : : VL_DO_DANGLING(nodep->deleteTree(), nodep);
7628 : : }
7629 : : }
7630 : : }
7631 : : void visit(AstISToRD* nodep) override {
7632 : : // Real: Output real
7633 : : // LHS presumed self-determined, then coerced to real
7634 : : assertAtExpr(nodep);
7635 : : if (m_vup->prelim()) { // First stage evaluation
7636 : : nodep->dtypeSetDouble();
7637 : : // Self-determined operand (TODO check if numeric type)
7638 : : userIterateAndNext(nodep->lhsp(), WidthVP{SELF, PRELIM}.p());
7639 : : }
7640 : : }
7641 : : void visit_Os32_Lr(AstNodeUniop* nodep) {
7642 : : // CALLER: RToI
7643 : : // Real: LHS real
7644 : : // LHS presumed self-determined, then coerced to real
7645 : : assertAtExpr(nodep);
7646 : : if (m_vup->prelim()) { // First stage evaluation
7647 : : iterateCheckReal(nodep, "LHS", nodep->lhsp(), BOTH);
7648 : : nodep->dtypeSetSigned32();
7649 : : }
7650 : : }
7651 : : void visit_Ou64_Lr(AstNodeUniop* nodep) {
7652 : : // CALLER: RealToBits
7653 : : // Real: LHS real
7654 : : // LHS presumed self-determined, then coerced to real
7655 : : assertAtExpr(nodep);
7656 : : if (m_vup->prelim()) { // First stage evaluation
7657 : : iterateCheckReal(nodep, "LHS", nodep->lhsp(), BOTH);
7658 : : nodep->dtypeSetUInt64();
7659 : : }
7660 : : }
7661 : :
7662 : : void visit_log_not(AstNode* nodep) {
7663 : : // CALLER: LogNot
7664 : : // Width-check: lhs 1 bit
7665 : : // Real: Allowed; implicitly compares with zero
7666 : : // We calculate the width of the UNDER expression.
7667 : : // We then check its width to see if it's legal, and edit if not
7668 : : // We finally set the width of our output
7669 : : // IEEE-2012: Table 11-21 and 11.8.1 (same as RedAnd):
7670 : : // LHS is self-determined
7671 : : // Width: 1 bit out
7672 : : // Sign: unsigned out (11.8.1)
7673 : : UASSERT_OBJ(!nodep->op2p(), nodep, "For unary ops only!");
7674 : : assertAtExpr(nodep);
7675 : : if (m_vup->prelim()) {
7676 : : iterateCheckBool(nodep, "LHS", nodep->op1p(), BOTH);
7677 : : nodep->dtypeSetBit();
7678 : : if (m_underSExpr) {
7679 : : nodep->v3error("Unexpected 'not' in sequence expression context");
7680 : : AstConst* const newp = new AstConst{nodep->fileline(), 0};
7681 : : newp->dtypeFrom(nodep);
7682 : : nodep->replaceWith(newp);
7683 : : VL_DO_DANGLING(pushDeletep(nodep), nodep);
7684 : : }
7685 : : }
7686 : : }
7687 : : void visit_log_and_or(AstNodeBiop* nodep) {
7688 : : // CALLER: LogAnd, LogOr, LogEq, LogIf
7689 : : // Widths: 1 bit out, lhs 1 bit, rhs 1 bit
7690 : : // IEEE-2012 Table 11-21:
7691 : : // LHS is self-determined
7692 : : // RHS is self-determined
7693 : : assertAtExpr(nodep);
7694 : : if (m_vup->prelim()) {
7695 : : iterateCheckBool(nodep, "LHS", nodep->lhsp(), BOTH);
7696 : : iterateCheckBool(nodep, "RHS", nodep->rhsp(), BOTH);
7697 : : nodep->dtypeSetBit();
7698 : : }
7699 : : }
7700 : : void visit_red_and_or(AstNodeUniop* nodep) {
7701 : : // CALLER: RedAnd, RedOr, ...
7702 : : // Signed: Output unsigned, Lhs/Rhs/etc non-real (presumed, not in IEEE)
7703 : : // IEEE-2012: Table 11-21 and 11.8.1:
7704 : : // LHS is self-determined
7705 : : // Width: 1 bit out
7706 : : // Sign: unsigned out (11.8.1)
7707 : : assertAtExpr(nodep);
7708 : : if (m_vup->prelim()) {
7709 : : iterateCheckSizedSelf(nodep, "LHS", nodep->lhsp(), SELF, BOTH);
7710 : : nodep->dtypeSetBit();
7711 : : }
7712 : : }
7713 : : void visit_red_unknown(AstNodeUniop* nodep) {
7714 : : // CALLER: IsUnknown
7715 : : // Signed: Output unsigned, Lhs/Rhs/etc non-real (presumed, not in IEEE)
7716 : : // IEEE-2012: Table 11-21 and 11.8.1:
7717 : : // LHS is self-determined
7718 : : // Width: 1 bit out
7719 : : // Sign: unsigned out (11.8.1)
7720 : : assertAtExpr(nodep);
7721 : : if (m_vup->prelim()) {
7722 : : userIterateAndNext(nodep->lhsp(), WidthVP{SELF, BOTH}.p());
7723 : : nodep->dtypeSetBit();
7724 : : }
7725 : : }
7726 : :
7727 : : // LRM 6.22.2 Equivalent types
7728 : : bool isEquivalentDType(const AstNodeDType* lhs, const AstNodeDType* rhs) {
7729 : : // a) If two types match, they are equivalent.
7730 : : if (!lhs || !rhs) return false;
7731 : : lhs = lhs->skipRefp();
7732 : : rhs = rhs->skipRefp();
7733 : : if (lhs == rhs) return true;
7734 : : // If both are basic types, check if they are the same type
7735 : : if (VN_IS(lhs, BasicDType) && VN_IS(rhs, BasicDType)) {
7736 : : const auto* lb = VN_CAST(lhs, BasicDType);
7737 : : const auto* rb = VN_CAST(rhs, BasicDType);
7738 : : if (lb->isString() != rb->isString()) return false;
7739 : : }
7740 : :
7741 : : // d) Unpacked fixed-size array types are equivalent if they have equivalent element types
7742 : : // and equal size; the actual range bounds may differ. Note that the element type of a
7743 : : // multidimensional array is itself an array type.
7744 : : const bool lhsIsUnpackArray = VN_IS(lhs, UnpackArrayDType);
7745 : : const bool rhsIsUnpackArray = VN_IS(rhs, UnpackArrayDType);
7746 : : if (lhsIsUnpackArray || rhsIsUnpackArray) {
7747 : : if (VN_IS(lhs, UnpackArrayDType) && VN_IS(rhs, UnpackArrayDType)) {
7748 : : const AstUnpackArrayDType* const lhsp = VN_CAST(lhs, UnpackArrayDType);
7749 : : const AstUnpackArrayDType* const rhsp = VN_CAST(rhs, UnpackArrayDType);
7750 : : const int lsz = lhsp->elementsConst();
7751 : : const int rsz = rhsp->elementsConst();
7752 : : if (lsz >= 0 && rsz >= 0 && lsz != rsz) return false;
7753 : : return isEquivalentDType(lhsp->subDTypep(), rhsp->subDTypep());
7754 : : }
7755 : : return false;
7756 : : }
7757 : :
7758 : : // e) Dynamic array, associative array, and queue types are equivalent if they are the same
7759 : : // kind of array (dynamic, associative, or queue), have equivalent index types (for
7760 : : // associative arrays), and have equivalent element types.
7761 : : const bool lhsIsDynArray = VN_IS(lhs, DynArrayDType);
7762 : : const bool rhsIsDynArray = VN_IS(rhs, DynArrayDType);
7763 : : const bool lhsIsQueue = VN_IS(lhs, QueueDType);
7764 : : const bool rhsIsQueue = VN_IS(rhs, QueueDType);
7765 : : const bool lhsIsAssocArray = VN_IS(lhs, AssocArrayDType);
7766 : : const bool rhsIsAssocArray = VN_IS(rhs, AssocArrayDType);
7767 : :
7768 : : if (lhsIsDynArray || rhsIsDynArray || lhsIsQueue || rhsIsQueue || lhsIsAssocArray
7769 : : || rhsIsAssocArray) {
7770 : : if (const AstDynArrayDType* const lhsp = VN_CAST(lhs, DynArrayDType)) {
7771 : : if (const AstDynArrayDType* const rhsp = VN_CAST(rhs, DynArrayDType)) {
7772 : : return isEquivalentDType(lhsp->subDTypep(), rhsp->subDTypep());
7773 : : }
7774 : : }
7775 : :
7776 : : if (const AstQueueDType* const lhsp = VN_CAST(lhs, QueueDType)) {
7777 : : if (const AstQueueDType* const rhsp = VN_CAST(rhs, QueueDType)) {
7778 : : return isEquivalentDType(lhsp->subDTypep(), rhsp->subDTypep());
7779 : : }
7780 : : }
7781 : :
7782 : : if (const AstAssocArrayDType* const lhsp = VN_CAST(lhs, AssocArrayDType)) {
7783 : : if (const AstAssocArrayDType* const rhsp = VN_CAST(rhs, AssocArrayDType)) {
7784 : : return isEquivalentDType(lhsp->subDTypep(), rhsp->subDTypep())
7785 : : && isEquivalentDType(lhsp->keyDTypep(), rhsp->keyDTypep());
7786 : : }
7787 : : }
7788 : :
7789 : : return false;
7790 : : }
7791 : :
7792 : : // c) Packed arrays, packed structures, packed unions, and built-in integral
7793 : : // types are equivalent if they contain the same number of total bits, are either all
7794 : : // 2-state or all 4-state, and are either all signed or all unsigned.
7795 : : if (lhs->isIntegralOrPacked() && rhs->isIntegralOrPacked()) {
7796 : : if (lhs->width() != rhs->width()) return false;
7797 : : if (lhs->isFourstate() != rhs->isFourstate()) return false;
7798 : : if (lhs->isSigned() != rhs->isSigned()) return false;
7799 : : return true;
7800 : : }
7801 : :
7802 : : return true;
7803 : : }
7804 : :
7805 : : static bool isAggregateType(const AstNode* nodep) {
7806 : : if (!nodep) return false;
7807 : : const AstNodeDType* dtypep = nodep->dtypep();
7808 : : if (!dtypep) return false;
7809 : : dtypep = dtypep->skipRefp();
7810 : : if (!dtypep) return false;
7811 : : return dtypep->isAggregateType();
7812 : : }
7813 : :
7814 : : void visit_cmp_eq_gt(AstNodeBiop* nodep, bool realok) {
7815 : : // CALLER: AstEq, AstGt, ..., AstLtS
7816 : : // Real allowed if and only if real_lhs set
7817 : : // See IEEE-2012 11.4.4, and 11.8.1:
7818 : : // Widths: 1 bit out, width is max of LHS or RHS
7819 : : // Sign: signed compare (not output) if both signed, compare is signed,
7820 : : // width mismatches sign extend
7821 : : // else, compare is unsigned, **zero-extends**
7822 : : // Real: If either real, other side becomes real and real compare
7823 : : // TODO: chandle/class handle/iface handle: WildEq/WildNeq same as Eq/Neq
7824 : : // TODO: chandle/class handle/iface handle only allowed to self-compare or against null
7825 : : // TODO: chandle/class handle/iface handle no relational compares
7826 : : UASSERT_OBJ(nodep->rhsp(), nodep, "For binary ops only!");
7827 : : assertAtExpr(nodep);
7828 : : if (m_vup->prelim()) {
7829 : : // Assignment patterns cannot self-determine their type.
7830 : : // Iterate the non-pattern operand first so its dtype is available,
7831 : : // then propagate that dtype to the pattern operand (analogous to
7832 : : // how assignments pass LHS dtype to RHS patterns).
7833 : : const bool lhsIsPat = VN_IS(nodep->lhsp(), Pattern);
7834 : : const bool rhsIsPat = VN_IS(nodep->rhsp(), Pattern);
7835 : : if (lhsIsPat && !rhsIsPat) {
7836 : : userIterateAndNext(nodep->rhsp(), WidthVP{CONTEXT_DET, PRELIM}.p());
7837 : : userIterateAndNext(nodep->lhsp(), WidthVP{nodep->rhsp()->dtypep(), PRELIM}.p());
7838 : : } else if (rhsIsPat && !lhsIsPat) {
7839 : : userIterateAndNext(nodep->lhsp(), WidthVP{CONTEXT_DET, PRELIM}.p());
7840 : : userIterateAndNext(nodep->rhsp(), WidthVP{nodep->lhsp()->dtypep(), PRELIM}.p());
7841 : : } else {
7842 : : userIterateAndNext(nodep->lhsp(), WidthVP{CONTEXT_DET, PRELIM}.p());
7843 : : userIterateAndNext(nodep->rhsp(), WidthVP{CONTEXT_DET, PRELIM}.p());
7844 : : }
7845 : :
7846 : : const bool isAggrLhs = isAggregateType(nodep->lhsp());
7847 : : const bool isAggrRhs = isAggregateType(nodep->rhsp());
7848 : :
7849 : : if ((isAggrLhs || isAggrRhs) && nodep->lhsp() && nodep->rhsp()) {
7850 : : const AstNodeDType* const lhsDType = nodep->lhsp()->dtypep();
7851 : : const AstNodeDType* const rhsDType = nodep->rhsp()->dtypep();
7852 : :
7853 : : if (lhsDType && rhsDType && !isEquivalentDType(lhsDType, rhsDType)) {
7854 : : nodep->v3error("Comparison requires matching data types\n"
7855 : : << nodep->warnMore() << "... Left-hand data type: "
7856 : : << lhsDType->prettyDTypeNameQ() << "\n"
7857 : : << nodep->warnMore() << "... Right-hand data type: "
7858 : : << rhsDType->prettyDTypeNameQ());
7859 : : AstNode* const newp = new AstConst{nodep->fileline(), AstConst::BitFalse{}};
7860 : : nodep->replaceWith(newp);
7861 : : VL_DO_DANGLING(pushDeletep(nodep), nodep);
7862 : : return;
7863 : : }
7864 : : } else if (nodep->lhsp()->isDouble() || nodep->rhsp()->isDouble()) {
7865 : : if (!realok) {
7866 : : nodep->v3error("Real is illegal operand to ?== operator");
7867 : : AstNode* const newp = new AstConst{nodep->fileline(), AstConst::BitFalse{}};
7868 : : nodep->replaceWith(newp);
7869 : : VL_DO_DANGLING(pushDeletep(nodep), nodep);
7870 : : return;
7871 : : }
7872 : : if (AstNodeBiop* const newp = replaceWithDVersion(nodep)) {
7873 : : VL_DANGLING(nodep);
7874 : : nodep = newp; // Process new node instead
7875 : : iterateCheckReal(nodep, "LHS", nodep->lhsp(), FINAL);
7876 : : iterateCheckReal(nodep, "RHS", nodep->rhsp(), FINAL);
7877 : : }
7878 : : } else if (nodep->lhsp()->isString() || nodep->rhsp()->isString()) {
7879 : : if (AstNodeBiop* const newp = replaceWithNVersion(nodep)) {
7880 : : VL_DANGLING(nodep);
7881 : : nodep = newp; // Process new node instead
7882 : : iterateCheckString(nodep, "LHS", nodep->lhsp(), FINAL);
7883 : : iterateCheckString(nodep, "RHS", nodep->rhsp(), FINAL);
7884 : : }
7885 : : } else {
7886 : : const bool signedFl = nodep->lhsp()->isSigned() && nodep->rhsp()->isSigned();
7887 : : if (AstNodeBiop* const newp = replaceWithUOrSVersion(nodep, signedFl)) {
7888 : : VL_DANGLING(nodep);
7889 : : nodep = newp; // Process new node instead
7890 : : }
7891 : : const int width = std::max(nodep->lhsp()->width(), nodep->rhsp()->width());
7892 : : const int ewidth = std::max(nodep->lhsp()->widthMin(), nodep->rhsp()->widthMin());
7893 : : AstNodeDType* const subDTypep
7894 : : = nodep->findLogicDType(width, ewidth, VSigning::fromBool(signedFl));
7895 : : bool warnOn = true;
7896 : : if (!signedFl && width == 32) {
7897 : : // Waive on unsigned < or <= if RHS is narrower, since can't give wrong answer
7898 : : if ((VN_IS(nodep, Lt) || VN_IS(nodep, Lte))
7899 : : && (nodep->lhsp()->width() >= nodep->rhsp()->widthMin())) {
7900 : : warnOn = false;
7901 : : }
7902 : : // Waive on unsigned > or >= if RHS is wider, since can't give wrong answer
7903 : : if ((VN_IS(nodep, Gt) || VN_IS(nodep, Gte))
7904 : : && (nodep->lhsp()->widthMin() >= nodep->rhsp()->width())) {
7905 : : warnOn = false;
7906 : : }
7907 : : }
7908 : : iterateCheck(nodep, "LHS", nodep->lhsp(), CONTEXT_DET, FINAL, subDTypep,
7909 : : (signedFl ? EXTEND_LHS : EXTEND_ZERO), warnOn);
7910 : : iterateCheck(nodep, "RHS", nodep->rhsp(), CONTEXT_DET, FINAL, subDTypep,
7911 : : (signedFl ? EXTEND_LHS : EXTEND_ZERO), warnOn);
7912 : : }
7913 : : nodep->dtypeSetBit();
7914 : : }
7915 : : }
7916 : : void visit_cmp_real(AstNodeBiop* nodep) {
7917 : : // CALLER: EqD, LtD
7918 : : // Widths: 1 bit out, lhs width == rhs width
7919 : : // Signed compare (not output) if both sides signed
7920 : : // Real if and only if real_allow set
7921 : : // IEEE, 11.4.4: relational compares (<,>,<=,>=,==,===,!=,!==) use
7922 : : // "zero padding" on unsigned
7923 : : UASSERT_OBJ(nodep->rhsp(), nodep, "For binary ops only!");
7924 : : assertAtExpr(nodep);
7925 : : if (m_vup->prelim()) {
7926 : : // See similar handling in visit_cmp_eq_gt where created
7927 : : iterateCheckReal(nodep, "LHS", nodep->lhsp(), BOTH);
7928 : : iterateCheckReal(nodep, "RHS", nodep->rhsp(), BOTH);
7929 : : nodep->dtypeSetBit();
7930 : : }
7931 : : }
7932 : : void visit_cmp_string(AstNodeBiop* nodep) {
7933 : : // CALLER: EqN, LtN
7934 : : // Widths: 1 bit out, lhs width == rhs width
7935 : : // String compare (not output)
7936 : : // Real if and only if real_allow set
7937 : : UASSERT_OBJ(nodep->rhsp(), nodep, "For binary ops only!");
7938 : : assertAtExpr(nodep);
7939 : : if (m_vup->prelim()) {
7940 : : // See similar handling in visit_cmp_eq_gt where created
7941 : : iterateCheckString(nodep, "LHS", nodep->lhsp(), BOTH);
7942 : : iterateCheckString(nodep, "RHS", nodep->rhsp(), BOTH);
7943 : : nodep->dtypeSetBit();
7944 : : }
7945 : : }
7946 : : void visit_cmp_type(AstNodeBiop* nodep) {
7947 : : // CALLER: EqT, LtT
7948 : : // Widths: 1 bit out
7949 : : // Data type compare (not output)
7950 : : assertAtExpr(nodep);
7951 : : if (m_vup->prelim()) {
7952 : : userIterateAndNext(nodep->lhsp(), WidthVP{SELF, BOTH}.p());
7953 : : userIterateAndNext(nodep->rhsp(), WidthVP{SELF, BOTH}.p());
7954 : : const AstAttrOf* const lhsap = VN_AS(nodep->lhsp(), AttrOf);
7955 : : const AstAttrOf* const rhsap = VN_AS(nodep->rhsp(), AttrOf);
7956 : : UASSERT_OBJ(lhsap->attrType() == VAttrType::TYPEID, lhsap,
7957 : : "Type compare expects type reference");
7958 : : UASSERT_OBJ(rhsap->attrType() == VAttrType::TYPEID, rhsap,
7959 : : "Type compare expects type reference");
7960 : : const AstNodeDType* const lhsDtp = lhsap->dtypep();
7961 : : const AstNodeDType* const rhsDtp = rhsap->dtypep();
7962 : : UINFO(9, "==type lhsDtp " << lhsDtp);
7963 : : UINFO(9, "==type rhsDtp " << lhsDtp);
7964 : : const bool invert = VN_IS(nodep, NeqT);
7965 : : const bool identical
7966 : : = AstNode::computeCastable(lhsDtp, rhsDtp, nodep) == VCastable::SAMEISH;
7967 : : UINFO(9, "== " << identical);
7968 : : const bool eq = invert ^ identical;
7969 : : AstNode* const newp = new AstConst{nodep->fileline(), AstConst::BitTrue{}, eq};
7970 : : nodep->replaceWith(newp);
7971 : : VL_DO_DANGLING(pushDeletep(nodep), nodep);
7972 : : }
7973 : : }
7974 : :
7975 : : void visit_negate_not(AstNodeUniop* nodep, bool real_ok) {
7976 : : // CALLER: (real_ok=false) Not
7977 : : // CALLER: (real_ok=true) Negate - allow real numbers
7978 : : // Signed: From lhs
7979 : : // IEEE-2012 Table 11-21:
7980 : : // Widths: out width = lhs width
7981 : : UASSERT_OBJ(!nodep->op2p(), nodep, "For unary ops only!");
7982 : : assertAtExpr(nodep);
7983 : : if (m_vup->prelim()) {
7984 : : userIterateAndNext(nodep->lhsp(), WidthVP{CONTEXT_DET, PRELIM}.p());
7985 : : if (!real_ok) checkCvtUS(nodep->lhsp(), false);
7986 : : }
7987 : : if (real_ok && nodep->lhsp()->isDouble()) {
7988 : : spliceCvtD(nodep->lhsp());
7989 : : if (AstNodeUniop* const newp = replaceWithDVersion(nodep)) {
7990 : : VL_DANGLING(nodep);
7991 : : nodep = newp; // Process new node instead
7992 : : iterateCheckReal(nodep, "LHS", nodep->lhsp(), BOTH);
7993 : : nodep->dtypeSetDouble();
7994 : : return;
7995 : : }
7996 : : } else {
7997 : : // Note there aren't yet uniops that need version changes
7998 : : // So no need to call replaceWithUOrSVersion(nodep, nodep->isSigned())
7999 : : }
8000 : : if (m_vup->prelim()) nodep->dtypeFrom(nodep->lhsp());
8001 : : if (m_vup->final()) {
8002 : : AstNodeDType* const expDTypep = m_vup->dtypeOverridep(nodep->dtypep());
8003 : : nodep->dtypep(expDTypep); // Propagate expression type to negation
8004 : : AstNodeDType* const subDTypep = expDTypep;
8005 : : // Some warning suppressions
8006 : : bool lhsWarn = true;
8007 : : if (VN_IS(nodep, Negate)) {
8008 : : // Warn if user wants extra bit from carry
8009 : : if (subDTypep->widthMin() == (nodep->lhsp()->widthMin() + 1)) lhsWarn = false;
8010 : : }
8011 : : iterateCheck(nodep, "LHS", nodep->lhsp(), CONTEXT_DET, FINAL, subDTypep, EXTEND_EXP,
8012 : : lhsWarn);
8013 : : }
8014 : : }
8015 : :
8016 : : void visit_signed_unsigned(AstNodeUniop* nodep, VSigning rs_out) {
8017 : : // CALLER: Signed, Unsigned
8018 : : // Width: lhs is self determined width
8019 : : // See IEEE-2012 6.24.1:
8020 : : // Width: Returns packed array, of size $bits(expression).
8021 : : // Sign: Output sign is as specified by operation
8022 : : // TODO: Type: Two-state if input is two-state, else four-state
8023 : : UASSERT_OBJ(!nodep->op2p(), nodep, "For unary ops only!");
8024 : : assertAtExpr(nodep);
8025 : : if (m_vup->prelim()) {
8026 : : userIterateAndNext(nodep->lhsp(), WidthVP{SELF, PRELIM}.p());
8027 : : checkCvtUS(nodep->lhsp(), true);
8028 : : const int width = nodep->lhsp()->width();
8029 : : AstNodeDType* const expDTypep = nodep->findLogicDType(width, width, rs_out);
8030 : : nodep->dtypep(expDTypep);
8031 : : AstNodeDType* const subDTypep = expDTypep;
8032 : : // The child's width is self determined
8033 : : iterateCheck(nodep, "LHS", nodep->lhsp(), SELF, FINAL, subDTypep, EXTEND_EXP);
8034 : : }
8035 : : }
8036 : :
8037 : : void visit_shift(AstNodeBiop* nodep) {
8038 : : // CALLER: ShiftL, ShiftR, ShiftRS
8039 : : // Widths: Output width from lhs, rhs<33 bits
8040 : : // Signed: Output signed iff LHS signed; unary operator
8041 : : // See IEEE 2012 11.4.10:
8042 : : // RHS is self-determined. RHS is always treated as unsigned, has no effect on result.
8043 : : iterate_shift_prelim(nodep);
8044 : : nodep->dtypeChgSigned(nodep->lhsp()->isSigned());
8045 : : const AstNodeBiop* const newp = iterate_shift_final(nodep);
8046 : : VL_DANGLING(nodep);
8047 : : (void)newp; // Ununused
8048 : : }
8049 : : void iterate_shift_prelim(AstNodeBiop* nodep) {
8050 : : // Shifts
8051 : : // See IEEE-2012 11.4.10 and Table 11-21.
8052 : : // RHS is self-determined. RHS is always treated as unsigned, has no effect on result.
8053 : : assertAtExpr(nodep);
8054 : : if (m_vup->prelim()) {
8055 : : userIterateAndNext(nodep->lhsp(), WidthVP{SELF, PRELIM}.p());
8056 : : checkCvtUS(nodep->lhsp(), false);
8057 : : iterateCheckSizedSelf(nodep, "RHS", nodep->rhsp(), SELF, BOTH);
8058 : : nodep->dtypeFrom(nodep->lhsp());
8059 : : }
8060 : : }
8061 : : AstNodeBiop* iterate_shift_final(AstNodeBiop* nodep) {
8062 : : // Nodep maybe edited
8063 : : assertAtExpr(nodep);
8064 : : if (m_vup->final()) {
8065 : : AstNodeDType* const expDTypep = m_vup->dtypeOverridep(nodep->dtypep());
8066 : : AstNodeDType* const subDTypep = expDTypep;
8067 : : nodep->dtypep(expDTypep);
8068 : : // ShiftRS converts to ShiftR, but not vice-versa
8069 : : if (VN_IS(nodep, ShiftRS)) {
8070 : : if (AstNodeBiop* const newp = replaceWithUOrSVersion(nodep, nodep->isSigned())) {
8071 : : VL_DANGLING(nodep);
8072 : : nodep = newp; // Process new node instead
8073 : : }
8074 : : }
8075 : : bool warnOn = true;
8076 : : // No warning if "X = 1'b1<<N"; assume user is doing what they want
8077 : : if (nodep->lhsp()->isOne() && VN_IS(nodep->backp(), NodeAssign)) warnOn = false;
8078 : : // We don't currently suppress these, as it's the upper operator (e.g. assign)
8079 : : // that reports the WIDTHEXPAND.
8080 : : AstConst* const shiftp = VN_CAST(nodep->rhsp(), Const);
8081 : : if (shiftp && !shiftp->num().isFourState() && shiftp->width() <= 32) {
8082 : : const int64_t shiftVal = shiftp->num().toSQuad();
8083 : : if (VN_IS(nodep, ShiftL)) {
8084 : : if (shiftVal > 0 && nodep->width() == nodep->lhsp()->width() + shiftVal)
8085 : : warnOn = false;
8086 : : }
8087 : : }
8088 : : iterateCheck(nodep, "LHS", nodep->lhsp(), CONTEXT_DET, FINAL, subDTypep, EXTEND_EXP,
8089 : : warnOn);
8090 : : if (nodep->rhsp()->width() > 32) {
8091 : : if (shiftp && shiftp->num().mostSetBitP1() <= 32) {
8092 : : // If (number)<<96'h1, then make it into (number)<<32'h1
8093 : : V3Number num{shiftp, 32, shiftp->num()};
8094 : : AstNode* const shiftrhsp = nodep->rhsp();
8095 : : nodep->rhsp()->replaceWith(new AstConst{shiftrhsp->fileline(), num});
8096 : : VL_DO_DANGLING(shiftrhsp->deleteTree(), shiftrhsp);
8097 : : }
8098 : : }
8099 : : }
8100 : : return nodep; // May edit
8101 : : }
8102 : :
8103 : : void visit_boolexpr_and_or(AstNodeBiop* nodep) {
8104 : : // CALLER: And, Or, Xor, ...
8105 : : // Lint widths: out width = lhs width = rhs width
8106 : : // Signed: if lhs & rhs signed
8107 : : // IEEE-2012 Table 11-21:
8108 : : // Width: max(LHS, RHS)
8109 : : UASSERT_OBJ(nodep->rhsp(), nodep, "For binary ops only!");
8110 : : // If errors are off, we need to follow the spec; thus we really need to do the max()
8111 : : // because the rhs could be larger, and we need to have proper editing to get the widths
8112 : : // to be the same for our operations.
8113 : : assertAtExpr(nodep);
8114 : : if (m_vup->prelim()) { // First stage evaluation
8115 : : // Determine expression widths only relying on what's in the subops
8116 : : userIterateAndNext(nodep->lhsp(), WidthVP{CONTEXT_DET, PRELIM}.p());
8117 : : userIterateAndNext(nodep->rhsp(), WidthVP{CONTEXT_DET, PRELIM}.p());
8118 : : checkCvtUS(nodep->lhsp(), false);
8119 : : checkCvtUS(nodep->rhsp(), false);
8120 : : const int width = std::max(nodep->lhsp()->width(), nodep->rhsp()->width());
8121 : : const int mwidth = std::max(nodep->lhsp()->widthMin(), nodep->rhsp()->widthMin());
8122 : : const bool expSigned = (nodep->lhsp()->isSigned() && nodep->rhsp()->isSigned());
8123 : : nodep->dtypeChgWidthSigned(width, mwidth, VSigning::fromBool(expSigned));
8124 : : }
8125 : : if (m_vup->final()) {
8126 : : AstNodeDType* const expDTypep = m_vup->dtypeOverridep(nodep->dtypep());
8127 : : AstNodeDType* const subDTypep = expDTypep;
8128 : : nodep->dtypep(expDTypep);
8129 : : // Error report and change sizes for suboperands of this node.
8130 : : iterateCheck(nodep, "LHS", nodep->lhsp(), CONTEXT_DET, FINAL, subDTypep, EXTEND_EXP);
8131 : : iterateCheck(nodep, "RHS", nodep->rhsp(), CONTEXT_DET, FINAL, subDTypep, EXTEND_EXP);
8132 : : }
8133 : : }
8134 : :
8135 : : void visit_add_sub_replace(AstNodeBiop* nodep, bool real_ok) {
8136 : : // CALLER: (real_ok=false) AddS, SubS, ...
8137 : : // CALLER: (real_ok=true) Add, Sub, ...
8138 : : // Widths: out width = lhs width = rhs width
8139 : : // Signed: Replace operator with signed operator, or signed to unsigned
8140 : : // Real: Replace operator with real operator
8141 : : // IEEE-2012 Table 11-21:
8142 : : // Width: max(LHS, RHS)
8143 : : // If errors are off, we need to follow the spec; thus we really need to do the max()
8144 : : // because the rhs could be larger, and we need to have proper editing to get the widths
8145 : : // to be the same for our operations.
8146 : : //
8147 : : // UINFOTREE(9, nodep, "rus " << m_vup, "rusin");
8148 : : assertAtExpr(nodep);
8149 : : if (m_vup->prelim()) { // First stage evaluation
8150 : : // Determine expression widths only relying on what's in the subops
8151 : : userIterateAndNext(nodep->lhsp(), WidthVP{CONTEXT_DET, PRELIM}.p());
8152 : : userIterateAndNext(nodep->rhsp(), WidthVP{CONTEXT_DET, PRELIM}.p());
8153 : : if (!real_ok) {
8154 : : checkCvtUS(nodep->lhsp(), false);
8155 : : checkCvtUS(nodep->rhsp(), false);
8156 : : }
8157 : : if (nodep->lhsp()->isDouble() || nodep->rhsp()->isDouble()) {
8158 : : spliceCvtD(nodep->lhsp());
8159 : : spliceCvtD(nodep->rhsp());
8160 : : if (AstNodeBiop* const newp = replaceWithDVersion(nodep)) {
8161 : : VL_DANGLING(nodep);
8162 : : nodep = newp; // Process new node instead
8163 : : }
8164 : : nodep->dtypeSetDouble();
8165 : : iterateCheckReal(nodep, "LHS", nodep->lhsp(), FINAL);
8166 : : iterateCheckReal(nodep, "RHS", nodep->rhsp(), FINAL);
8167 : : return;
8168 : : } else if (nodep->lhsp()->isString() || nodep->rhsp()->isString()) {
8169 : : nodep->v3error(
8170 : : "Operator "
8171 : : << nodep->prettyTypeName()
8172 : : << " is not legal on string data types (IEEE 1800-2023 6.16)\n"
8173 : : << (VN_IS(nodep, Add)
8174 : : ? (nodep->warnMore()
8175 : : + "... Suggest to concatenate strings use '{LHS, RHS, ...}'")
8176 : : : ""));
8177 : : nodep->replaceWith(new AstConst{nodep->fileline(), AstConst::String{}, ""});
8178 : : VL_DO_DANGLING(pushDeletep(nodep), nodep);
8179 : : return;
8180 : : } else {
8181 : : const int width = std::max(nodep->lhsp()->width(), nodep->rhsp()->width());
8182 : : const int mwidth = std::max(nodep->lhsp()->widthMin(), nodep->rhsp()->widthMin());
8183 : : const bool expSigned = (nodep->lhsp()->isSigned() && nodep->rhsp()->isSigned());
8184 : : nodep->dtypeChgWidthSigned(width, mwidth, VSigning::fromBool(expSigned));
8185 : : }
8186 : : }
8187 : : if (m_vup->final()) {
8188 : : // Parent's data type was computed using the max(upper, nodep->dtype)
8189 : : AstNodeDType* const expDTypep = m_vup->dtypeOverridep(nodep->dtypep());
8190 : : AstNodeDType* const subDTypep = expDTypep;
8191 : : nodep->dtypep(expDTypep);
8192 : : // We don't use LHS && RHS -- unspecified language corner, see t_math_signed5 test
8193 : : // bool expSigned = (nodep->lhsp()->isSigned() && nodep->rhsp()->isSigned());
8194 : : if (AstNodeBiop* const newp = replaceWithUOrSVersion(nodep, expDTypep->isSigned())) {
8195 : : VL_DANGLING(nodep);
8196 : : nodep = newp; // Process new node instead
8197 : : }
8198 : : // Some warning suppressions
8199 : : bool lhsWarn = true;
8200 : : bool rhsWarn = true;
8201 : : if (VN_IS(nodep, Add) || VN_IS(nodep, Sub)) {
8202 : : // Warn if user wants extra bit from carry
8203 : : if (subDTypep->widthMin() == (nodep->lhsp()->widthMin() + 1)) lhsWarn = false;
8204 : : if (subDTypep->widthMin() == (nodep->rhsp()->widthMin() + 1)) rhsWarn = false;
8205 : : if (VN_IS(nodep, Add) && nodep->lhsp()->width() == 1
8206 : : && nodep->rhsp()->width() != 1)
8207 : : lhsWarn = false; // do_increment + ...
8208 : : if (nodep->rhsp()->width() == 1 && nodep->lhsp()->width() != 1)
8209 : : rhsWarn = false; // ... + do_increment
8210 : : } else if (VN_IS(nodep, Mul) || VN_IS(nodep, MulS)) {
8211 : : if (subDTypep->widthMin() >= (nodep->lhsp()->widthMin())) lhsWarn = false;
8212 : : if (subDTypep->widthMin() >= (nodep->rhsp()->widthMin())) rhsWarn = false;
8213 : : }
8214 : : // Final call, so make sure children check their sizes
8215 : : // Error report and change sizes for suboperands of this node.
8216 : : iterateCheck(nodep, "LHS", nodep->lhsp(), CONTEXT_DET, FINAL, subDTypep, EXTEND_EXP,
8217 : : lhsWarn);
8218 : : iterateCheck(nodep, "RHS", nodep->rhsp(), CONTEXT_DET, FINAL, subDTypep, EXTEND_EXP,
8219 : : rhsWarn);
8220 : : }
8221 : : // UINFOTREE(9, nodep, "", "rusou");
8222 : : }
8223 : : void visit_real_add_sub(AstNodeBiop* nodep) {
8224 : : // CALLER: AddD, MulD, ...
8225 : : assertAtExpr(nodep);
8226 : : if (m_vup->prelim()) { // First stage evaluation
8227 : : // Note similar steps in visit_add_sub_replace promotion to double
8228 : : iterateCheckReal(nodep, "LHS", nodep->lhsp(), BOTH);
8229 : : iterateCheckReal(nodep, "RHS", nodep->rhsp(), BOTH);
8230 : : nodep->dtypeSetDouble();
8231 : : }
8232 : : }
8233 : : void visit_real_neg_ceil(AstNodeUniop* nodep) {
8234 : : // CALLER: Negate, Ceil, Log, ...
8235 : : assertAtExpr(nodep);
8236 : : if (m_vup->prelim()) { // First stage evaluation
8237 : : // See alsl visit_negate_not conversion
8238 : : iterateCheckReal(nodep, "LHS", nodep->lhsp(), BOTH);
8239 : : nodep->dtypeSetDouble();
8240 : : }
8241 : : }
8242 : :
8243 : : void visit_sformatf_format(AstSFormatF* nodep) {
8244 : : // For sformatf's with constant format, iterate/check arguments
8245 : : UASSERT_OBJ(!nodep->exprFormat(), nodep, "Assumes constant format");
8246 : : bool inPct = false;
8247 : : AstNodeExpr* argp = nodep->exprsp();
8248 : : string newFormat;
8249 : : for (char ch : nodep->text()) {
8250 : : if (!inPct && ch == '%') {
8251 : : inPct = true;
8252 : : newFormat += ch;
8253 : : } else if (inPct && (std::isdigit(ch) || ch == '.' || ch == '-')) {
8254 : : newFormat += ch;
8255 : : } else if (!inPct) { // Normal text
8256 : : newFormat += ch;
8257 : : } else {
8258 : : inPct = false;
8259 : : AstNodeExpr* const nextp = argp ? VN_AS(argp->nextp(), NodeExpr) : nullptr;
8260 : : AstSFormatArg* const fargp = VN_CAST(argp, SFormatArg); // May not exist yet
8261 : : AstNodeExpr* const subargp = fargp ? fargp->exprp() : argp;
8262 : : const AstNodeDType* const dtypep
8263 : : = subargp ? subargp->dtypep()->skipRefp() : nullptr;
8264 : : ch = std::tolower(ch);
8265 : : switch (ch) {
8266 : : case '?': // Unspecified by user, guess
8267 : : if (dtypep->isDouble()) {
8268 : : ch = 'g';
8269 : : } else if (dtypep->isString()) {
8270 : : ch = 's';
8271 : : } else if (VN_IS(dtypep, BasicDType)) {
8272 : : ch = nodep->missingArgChar();
8273 : : } else {
8274 : : ch = 'p';
8275 : : }
8276 : : argp = nextp;
8277 : : break;
8278 : : case '%': break; // %% - just output a %
8279 : : case 'm': break; // %m - auto insert "name"
8280 : : case 'l': break; // %m - auto insert "library"
8281 : : case 'd':
8282 : : if (subargp) {
8283 : : checkFormatNumericArg(subargp, ch);
8284 : : if (subargp->isDouble()) spliceCvtS(subargp, true, 64);
8285 : : argp = nextp;
8286 : : }
8287 : : break;
8288 : : case 'b': // FALLTHRU
8289 : : case 'o': // FALLTHRU
8290 : : case 'x':
8291 : : if (subargp) {
8292 : : // V3Randomize may make a %x on a string, or may
8293 : : // have a runtime format which gets past this error
8294 : : checkFormatNumericArg(subargp, ch);
8295 : : argp = nextp;
8296 : : }
8297 : : break;
8298 : : case 't': // Decimal/float time
8299 : : if (subargp) {
8300 : : checkFormatNumericArg(subargp, ch);
8301 : : UASSERT_OBJ(!nodep->timeunit().isNone(), nodep,
8302 : : "display %t has no time units");
8303 : : argp = nextp;
8304 : : }
8305 : : break;
8306 : : case 'e': // FALLTHRU
8307 : : case 'f': // FALLTHRU
8308 : : case 'g':
8309 : : if (subargp) {
8310 : : checkFormatNumericArg(subargp, ch);
8311 : : if (!subargp->isDouble()) {
8312 : : iterateCheckReal(nodep, "Display argument", subargp, BOTH);
8313 : : }
8314 : : argp = nextp;
8315 : : }
8316 : : break;
8317 : : case 'p': // FALLTHRU
8318 : : case 's': // FALLTHRU
8319 : : default: // Most operators, just move to next argument
8320 : : argp = nextp;
8321 : : break;
8322 : : } // switch
8323 : : newFormat += ch;
8324 : : }
8325 : : }
8326 : : nodep->text(newFormat);
8327 : : }
8328 : :
8329 : : //----------------------------------------------------------------------
8330 : : // LOWER LEVEL WIDTH METHODS (none iterate)
8331 : :
8332 : : bool widthBad(AstNode* nodep, AstNodeDType* expDTypep) {
8333 : : const int expWidth = expDTypep->width();
8334 : : int expWidthMin = expDTypep->widthMin();
8335 : : UASSERT_OBJ(nodep->dtypep(), nodep,
8336 : : "Under node " << nodep->prettyTypeName()
8337 : : << " has no dtype?? Missing Visitor func?");
8338 : : if (expDTypep->basicp()->untyped() || nodep->dtypep()->basicp()->untyped()) return false;
8339 : : // During DepGraph execution, expected width may be 0 if the type hasn't been
8340 : : UASSERT_OBJ(nodep->width() != 0, nodep,
8341 : : "Under node " << nodep->prettyTypeName()
8342 : : << " has no expected width?? Missing Visitor func?");
8343 : : UASSERT_OBJ(expWidth != 0, nodep,
8344 : : "Node " << nodep->prettyTypeName()
8345 : : << " has no expected width?? Missing Visitor func?");
8346 : : if (expWidthMin == 0) expWidthMin = expWidth;
8347 : : if (nodep->dtypep()->width() == expWidth) return false;
8348 : : if (nodep->dtypep()->widthSized() && nodep->width() != expWidthMin) return true;
8349 : : if (!nodep->dtypep()->widthSized() && nodep->widthMin() > expWidthMin) return true;
8350 : : return false;
8351 : : }
8352 : :
8353 : : void fixWidthExtend(AstNodeExpr* nodep, AstNodeDType* expDTypep, ExtendRule extendRule) {
8354 : : // Fix the width mismatch by extending or truncating bits
8355 : : // *ONLY* call this from checkWidth()
8356 : : // Truncation is rarer, but can occur: parameter [3:0] FOO = 64'h12312;
8357 : : // A(CONSTwide)+B becomes A(CONSTwidened)+B
8358 : : // A(somewide)+B becomes A(TRUNC(somewide,width))+B
8359 : : // or A(EXTRACT(somewide,width,0))+B
8360 : : // Sign extension depends on the type of the *present*
8361 : : // node, while the output dtype is the *expected* sign.
8362 : : // It is reasonable to have sign extension with unsigned output,
8363 : : // for example $unsigned(a)+$signed(b), the SIGNED(B) will be unsigned dtype out
8364 : : UINFO(4, " widthExtend_(r=" << static_cast<int>(extendRule) << ") old: " << nodep);
8365 : : if (extendRule == EXTEND_OFF) return;
8366 : : AstConst* const constp = VN_CAST(nodep, Const);
8367 : : const int expWidth = expDTypep->width();
8368 : : if (constp && !constp->num().isNegative()) {
8369 : : // Save later constant propagation work, just right-size it.
8370 : : V3Number num{nodep, expWidth, constp->num()};
8371 : : num.isSigned(false);
8372 : : AstNodeExpr* const newp = new AstConst{nodep->fileline(), num};
8373 : : constp->replaceWith(newp);
8374 : : VL_DO_DANGLING(pushDeletep(constp), constp);
8375 : : VL_DANGLING(nodep);
8376 : : nodep = newp;
8377 : : } else if (expWidth < nodep->width()) {
8378 : : // Trunc - Extract
8379 : : VNRelinker linker;
8380 : : nodep->unlinkFrBack(&linker);
8381 : : AstNodeExpr* const newp = new AstSel{nodep->fileline(), nodep, 0, expWidth};
8382 : : newp->didWidth(true); // Don't replace dtype with unsigned
8383 : : linker.relink(newp);
8384 : : nodep = newp;
8385 : : } else {
8386 : : // Extend
8387 : : VNRelinker linker;
8388 : : nodep->unlinkFrBack(&linker);
8389 : : bool doSigned = false;
8390 : : switch (extendRule) {
8391 : : case EXTEND_ZERO: doSigned = false; break;
8392 : : case EXTEND_EXP: doSigned = nodep->isSigned() && expDTypep->isSigned(); break;
8393 : : case EXTEND_LHS: doSigned = nodep->isSigned(); break;
8394 : : default: nodep->v3fatalSrc("bad case");
8395 : : }
8396 : : AstNodeExpr* const newp
8397 : : = (doSigned ? static_cast<AstNodeExpr*>(new AstExtendS{nodep->fileline(), nodep})
8398 : : : static_cast<AstNodeExpr*>(new AstExtend{nodep->fileline(), nodep}));
8399 : : newp->didWidth(true);
8400 : : linker.relink(newp);
8401 : : nodep = newp;
8402 : : }
8403 : : if (expDTypep->isDouble() && !nodep->isDouble()) {
8404 : : // For AstVar init() among others
8405 : : // TODO do all to-real and to-integer conversions in this function
8406 : : // rather than in callers
8407 : : AstNodeExpr* const newp = spliceCvtD(nodep);
8408 : : nodep = newp;
8409 : : }
8410 : : nodep->dtypep(expDTypep);
8411 : : UINFO(4, " _new: " << nodep);
8412 : : }
8413 : :
8414 : : void fixWidthReduce(AstNodeExpr* nodep) {
8415 : : // Fix the width mismatch by adding a reduction OR operator
8416 : : // IF (A(CONSTwide)) becomes IF (A(CONSTreduced))
8417 : : // IF (A(somewide)) on pointer becomes on pointer IF (A(NEQ(somewide, Null)))
8418 : : // IF (A(somewide)) elsewise becomes IF (A(REDOR(somewide, 0)))
8419 : : // Attempt to fix it quietly
8420 : : const int expWidth = 1;
8421 : : const int expSigned = false;
8422 : : UINFO(4, " widthReduce_old: " << nodep);
8423 : : AstConst* const constp = VN_CAST(nodep, Const);
8424 : : if (constp) {
8425 : : V3Number num(nodep, expWidth);
8426 : : num.opRedOr(constp->num());
8427 : : num.isSigned(expSigned);
8428 : : AstNodeExpr* const newp = new AstConst{nodep->fileline(), num};
8429 : : constp->replaceWith(newp);
8430 : : VL_DO_DANGLING(constp->deleteTree(), constp);
8431 : : VL_DANGLING(nodep);
8432 : : nodep = newp;
8433 : : } else {
8434 : : VNRelinker linker;
8435 : : nodep->unlinkFrBack(&linker);
8436 : : AstNodeExpr* newp;
8437 : : AstNodeDType* const dtypeSkipp = nodep->dtypep()->skipRefp();
8438 : : if (VN_IS(dtypeSkipp, ClassRefDType) || VN_IS(dtypeSkipp, IfaceRefDType)) {
8439 : : // Cannot use AstRedOr, as we may be redurcing a class handle
8440 : : newp = new AstNeq{nodep->fileline(),
8441 : : new AstConst{nodep->fileline(), AstConst::Null{}},
8442 : :
8443 : : nodep};
8444 : : } else {
8445 : : // But, AstRedOr can be faster on wide
8446 : : newp = new AstRedOr{nodep->fileline(), nodep};
8447 : : }
8448 : : linker.relink(newp);
8449 : : nodep = newp;
8450 : : }
8451 : : nodep->dtypeChgWidthSigned(expWidth, expWidth, VSigning::fromBool(expSigned));
8452 : : UINFO(4, " _new: " << nodep);
8453 : : }
8454 : :
8455 : : bool fixAutoExtend(AstNodeExpr*& nodepr, int expWidth) {
8456 : : // For SystemVerilog '0,'1,'x,'z, autoextend and don't warn
8457 : : if (AstConst* const constp = VN_CAST(nodepr, Const)) {
8458 : : if (constp->num().autoExtend() && !constp->num().sized() && constp->width() == 1) {
8459 : : // Make it the proper size. Careful of proper extension of 0's/1's
8460 : : V3Number num(constp, expWidth);
8461 : : num.opRepl(constp->num(), expWidth); // {width{'1}}
8462 : : AstNodeExpr* const newp = new AstConst{constp->fileline(), num};
8463 : : // Spec says always unsigned with proper width
8464 : : UINFOTREE(5, constp, "", "fixAutoExtend_old");
8465 : : UINFOTREE(5, newp, "", "_new");
8466 : : constp->replaceWith(newp);
8467 : : VL_DO_DANGLING(constp->deleteTree(), constp);
8468 : : // Tell caller the new constp, and that we changed it.
8469 : : nodepr = newp;
8470 : : return true;
8471 : : }
8472 : : // X/Z also upper bit extend. In pre-SV only to 32-bits, SV forever.
8473 : : else if (!constp->num().sized()
8474 : : // Make it the proper size. Careful of proper extension of 0's/1's
8475 : : && expWidth > 32 && constp->num().isMsbXZ()) {
8476 : : constp->v3warn(WIDTHXZEXPAND, "Unsized constant being X/Z extended to "
8477 : : << expWidth
8478 : : << " bits: " << constp->prettyName());
8479 : : V3Number num(constp, expWidth);
8480 : : num.opExtendXZ(constp->num(), constp->width());
8481 : : AstNodeExpr* const newp = new AstConst{constp->fileline(), num};
8482 : : // Spec says always unsigned with proper width
8483 : : UINFOTREE(5, constp, "", "fixUnszExtend_old");
8484 : : UINFOTREE(5, newp, "", "_new");
8485 : : constp->replaceWith(newp);
8486 : : VL_DO_DANGLING(constp->deleteTree(), constp);
8487 : : // Tell caller the new constp, and that we changed it.
8488 : : nodepr = newp;
8489 : : return true;
8490 : : }
8491 : : }
8492 : : return false; // No change
8493 : : }
8494 : : bool isBaseClassRecurse(const AstClass* const cls1p, const AstClass* const cls2p) {
8495 : : // Returns true if cls1p and cls2p are equal or if cls1p is a base class of cls2p
8496 : : if (cls1p == cls2p) return true;
8497 : : for (const AstClassExtends* cextp = cls2p->extendsp(); cextp;
8498 : : cextp = VN_CAST(cextp->nextp(), ClassExtends)) {
8499 : : if (isBaseClassRecurse(cls1p, cextp->classp())) return true;
8500 : : }
8501 : : return false;
8502 : : }
8503 : : // Checks whether two types are assignment-compatible according to IEEE 1800-2023 7.6
8504 : : // Currently, this function only supports variables, which are of following types:
8505 : : // - Fixed-size unpacked array
8506 : : // - Dynamic unpacked array
8507 : : // - Associative array
8508 : : template <typename T, typename N>
8509 : : void checkUnpackedArrayAssignmentCompatible(const AstNode* nodep, const T* const lhsRefp,
8510 : : const N* const rhsRefp) {
8511 : : static_assert(
8512 : : (std::is_same<T, AstVar>::value || std::is_same<T, AstNodeVarRef>::value)
8513 : : && (std::is_same<N, AstVar>::value || std::is_same<N, AstNodeVarRef>::value),
8514 : : "Unsupported types provided.");
8515 : : if (!lhsRefp || !rhsRefp) return;
8516 : : std::string lhsName;
8517 : : std::string rhsName;
8518 : : if (VN_IS(nodep, Pin)) {
8519 : : lhsName = std::string{"Pin"};
8520 : : rhsName = std::string{"Expression"};
8521 : : } else {
8522 : : lhsName = std::string{"Left-hand"};
8523 : : rhsName = std::string{"Right-hand"};
8524 : : }
8525 : :
8526 : : const AstNodeDType* const lhsDtp = lhsRefp->dtypep()->skipRefp();
8527 : : const AstNodeDType* const rhsDtp = rhsRefp->dtypep()->skipRefp();
8528 : : const bool isLhsAggregate = lhsDtp->isAggregateType();
8529 : : const bool isRhsAggregate = rhsDtp->isAggregateType();
8530 : : if (!isLhsAggregate && !isRhsAggregate) return;
8531 : : if (isLhsAggregate ^ isRhsAggregate) {
8532 : : nodep->v3error(
8533 : : "Illegal assignment: types are not assignment compatible (IEEE 1800-2023 7.6)\n"
8534 : : << nodep->warnMore() << "... " << lhsName << " data type: "
8535 : : << lhsDtp->prettyDTypeNameQ() << " " << lhsDtp->stateDTypeName() << "\n"
8536 : : << nodep->warnMore() << "... " << rhsName << " data type: "
8537 : : << rhsDtp->prettyDTypeNameQ() << " " << rhsDtp->stateDTypeName() << "\n");
8538 : : return;
8539 : : } else if (VN_IS(lhsDtp, QueueDType) && VN_IS(rhsDtp, EmptyQueueDType)) {
8540 : : return;
8541 : : }
8542 : : std::pair<uint32_t, uint32_t> lhsDim = lhsDtp->dimensions(false),
8543 : : rhsDim = rhsDtp->dimensions(false);
8544 : : // Check if unpacked array dimensions are matching
8545 : : if (lhsDim.second != rhsDim.second) {
8546 : : nodep->v3error("Illegal assignment: Unmatched number of unpacked dimensions "
8547 : : << "(" << lhsDim.second << " vs " << rhsDim.second << ")");
8548 : : return;
8549 : : }
8550 : :
8551 : : const AstNodeDType* lhsDtpIterp = lhsDtp;
8552 : : const AstNodeDType* rhsDtpIterp = rhsDtp;
8553 : : // Sizes of fixed-size arrays should be the same
8554 : : // Dynamic-sized arrays are always assignable
8555 : : for (uint32_t dim = 0; dim < rhsDim.second; dim++) {
8556 : : if (const AstNodeArrayDType* rhsArrayp = VN_CAST(rhsDtpIterp, NodeArrayDType)) {
8557 : : if (const AstNodeArrayDType* lhsArrayp = VN_CAST(lhsDtpIterp, NodeArrayDType)) {
8558 : : if (lhsArrayp->elementsConst() != rhsArrayp->elementsConst()) {
8559 : : nodep->v3error("Illegal assignment: Unmatched array sizes in dimension "
8560 : : << dim << " " << "(" << lhsArrayp->elementsConst() << " vs "
8561 : : << rhsArrayp->elementsConst() << ")");
8562 : : return;
8563 : : }
8564 : : }
8565 : : }
8566 : : // Associative arrays are compatible only with each other
8567 : : if (VN_IS(lhsDtpIterp, AssocArrayDType) ^ VN_IS(rhsDtpIterp, AssocArrayDType)) {
8568 : : nodep->v3error("Illegal assignment: Associative arrays are assignment compatible "
8569 : : "only with associative arrays (IEEE 1800-2023 7.6)");
8570 : :
8571 : : return;
8572 : : }
8573 : : lhsDtpIterp = lhsDtpIterp->subDTypep();
8574 : : rhsDtpIterp = rhsDtpIterp->subDTypep();
8575 : : }
8576 : : // Element types of source and target shall be equivalent
8577 : : if (!isEquivalentDType(lhsDtpIterp, rhsDtpIterp)) {
8578 : : nodep->v3error("Illegal assignment: Array element types are not equivalent (IEEE "
8579 : : "1800-2023 6.22.2)\n"
8580 : : << nodep->warnMore() << "... " << lhsName << " data type: "
8581 : : << lhsDtp->prettyDTypeNameQ() << " " << lhsDtp->stateDTypeName() << "\n"
8582 : : << nodep->warnMore() << "... " << rhsName
8583 : : << " data type: " << rhsDtp->prettyDTypeNameQ() << " "
8584 : : << rhsDtp->stateDTypeName() << "\n");
8585 : : }
8586 : : }
8587 : : void checkClassAssign(const AstNode* nodep, const char* side, AstNode* rhsp,
8588 : : AstNodeDType* const lhsDTypep, bool isOutputArg = false) {
8589 : : UASSERT_OBJ(rhsp->dtypep(), rhsp, "Node has no type");
8590 : : const AstNodeDType* const lhsRawDTypep = lhsDTypep->skipRefp();
8591 : : const AstNodeDType* const rhsRawDTypep = rhsp->dtypep()->skipRefp();
8592 : : if (const AstClassRefDType* const lhsClassRefp = VN_CAST(lhsRawDTypep, ClassRefDType)) {
8593 : : if (const AstClassRefDType* const rhsClassRefp
8594 : : = VN_CAST(rhsRawDTypep, ClassRefDType)) {
8595 : : // Output arg swaps sides: caller actual = callee formal at return.
8596 : : const AstClass* const dstp
8597 : : = isOutputArg ? rhsClassRefp->classp() : lhsClassRefp->classp();
8598 : : const AstClass* const srcp
8599 : : = isOutputArg ? lhsClassRefp->classp() : rhsClassRefp->classp();
8600 : : if (isBaseClassRecurse(dstp, srcp)) return;
8601 : : } else if (rhsp->isNull()) {
8602 : : return;
8603 : : }
8604 : : nodep->v3error(side << " expects a " << lhsClassRefp->prettyTypeName() << ", got "
8605 : : << rhsRawDTypep->prettyTypeName());
8606 : : } else if (VN_IS(rhsRawDTypep, ClassRefDType)) {
8607 : : nodep->v3error(side << " " << rhsRawDTypep->prettyDTypeNameQ()
8608 : : << " cannot be assigned to non-class "
8609 : : << lhsDTypep->prettyDTypeNameQ());
8610 : : }
8611 : : if (VN_IS(lhsRawDTypep, DynArrayDType) && VN_IS(rhsRawDTypep, UnpackArrayDType)) {
8612 : : VNRelinker relinker;
8613 : : rhsp->unlinkFrBack(&relinker);
8614 : : relinker.relink(
8615 : : new AstCvtUnpackedToQueue{rhsp->fileline(), VN_AS(rhsp, NodeExpr), lhsDTypep});
8616 : : }
8617 : : }
8618 : : static bool similarDTypeRecurse(const AstNodeDType* const node1p,
8619 : : const AstNodeDType* const node2p) {
8620 : : return node1p->skipRefp()->similarDType(node2p->skipRefp());
8621 : : }
8622 : : void iterateCheckFileDesc(AstNode* parentp, AstNode* underp, Stage stage) {
8623 : : UASSERT_OBJ(stage == BOTH, parentp, "Bad call");
8624 : : // underp may change as a result of replacement
8625 : : underp = userIterateSubtreeReturnEdits(underp, WidthVP{SELF, PRELIM}.p());
8626 : : AstNodeDType* const expDTypep = underp->findUInt32DType();
8627 : : underp
8628 : : = iterateCheck(parentp, "file_descriptor", underp, SELF, FINAL, expDTypep, EXTEND_EXP);
8629 : : (void)underp; // cppcheck
8630 : : }
8631 : : void iterateCheckReal(AstNode* parentp, const char* side, AstNode* underp, Stage stage) {
8632 : : // Coerce child to real if not already. Child is self-determined
8633 : : // e.g. parentp=ADDD, underp=ADD in ADDD(ADD(a,b), real-CONST)
8634 : : // Don't need separate PRELIM and FINAL(double) calls;
8635 : : // as if resolves to double, the BOTH correctly resolved double,
8636 : : // otherwise self-determined was correct
8637 : : iterateCheckTypedSelfPrelim(parentp, side, underp, parentp->findDoubleDType(), stage);
8638 : : }
8639 : : void iterateCheckSigned8(AstNode* parentp, const char* side, AstNode* underp, Stage stage) {
8640 : : // Coerce child to signed8 if not already. Child is self-determined
8641 : : iterateCheckTypedSelfPrelim(parentp, side, underp, parentp->findSigned8DType(), stage);
8642 : : }
8643 : : void iterateCheckSigned32(AstNode* parentp, const char* side, AstNode* underp, Stage stage) {
8644 : : // Coerce child to signed32 if not already. Child is self-determined
8645 : : iterateCheckTypedSelfPrelim(parentp, side, underp, parentp->findSigned32DType(), stage);
8646 : : }
8647 : : void iterateCheckUInt32(AstNode* parentp, const char* side, AstNode* underp, Stage stage) {
8648 : : // Coerce child to unsigned32 if not already. Child is self-determined
8649 : : iterateCheckTypedSelfPrelim(parentp, side, underp, parentp->findUInt32DType(), stage);
8650 : : }
8651 : : void iterateCheckDelay(AstNode* parentp, const char* side, AstNode* underp, Stage stage) {
8652 : : // Coerce child to 64-bit delay if not already. Child is self-determined
8653 : : // underp may change as a result of replacement
8654 : : if (stage & PRELIM) {
8655 : : underp = userIterateSubtreeReturnEdits(underp, WidthVP{SELF, PRELIM}.p());
8656 : : }
8657 : : if (stage & FINAL) {
8658 : : AstNodeDType* expDTypep;
8659 : : if (underp->dtypep()->skipRefp()->isDouble()) { // V3Timing will later convert double
8660 : : expDTypep = parentp->findDoubleDType();
8661 : : } else {
8662 : : FileLine* const newFl = new FileLine{underp->fileline()};
8663 : : newFl->warnOff(V3ErrorCode::WIDTHEXPAND, true);
8664 : : underp->fileline(newFl);
8665 : : expDTypep = parentp->findLogicDType(64, 64, VSigning::UNSIGNED);
8666 : : }
8667 : : underp
8668 : : = iterateCheck(parentp, side, underp, SELF, FINAL, expDTypep, EXTEND_EXP, false);
8669 : : }
8670 : : (void)underp; // cppcheck
8671 : : }
8672 : : void iterateCheckString(AstNode* parentp, const char* side, AstNode* underp, Stage stage) {
8673 : : iterateCheckTyped(parentp, side, underp, parentp->findStringDType(), stage);
8674 : : }
8675 : : void iterateCheckTyped(AstNode* parentp, const char* side, AstNode* underp,
8676 : : AstNodeDType* expDTypep, Stage stage) {
8677 : : if (stage & PRELIM) {
8678 : : underp = userIterateSubtreeReturnEdits(underp, WidthVP{expDTypep, PRELIM}.p());
8679 : : }
8680 : : if (stage & FINAL) {
8681 : : underp = iterateCheck(parentp, side, underp, SELF, FINAL, expDTypep, EXTEND_EXP);
8682 : : }
8683 : : (void)underp; // cppcheck
8684 : : }
8685 : : void iterateCheckTypedSelfPrelim(AstNode* parentp, const char* side, AstNode* underp,
8686 : : AstNodeDType* expDTypep, Stage stage) {
8687 : : // underp may change as a result of replacement
8688 : : if (stage & PRELIM) {
8689 : : underp = userIterateSubtreeReturnEdits(underp, WidthVP{SELF, PRELIM}.p());
8690 : : }
8691 : : if (stage & FINAL) {
8692 : : underp = iterateCheck(parentp, side, underp, SELF, FINAL, expDTypep, EXTEND_EXP);
8693 : : }
8694 : : (void)underp; // cppcheck
8695 : : }
8696 : : void iterateCheckIntegralSelf(AstNode* parentp, const char* side, AstNode* underp,
8697 : : Determ determ, Stage stage) {
8698 : : iterateCheckSelf(parentp, side, underp, determ, stage, true);
8699 : : }
8700 : : void iterateCheckSelf(AstNode* parentp, const char* side, AstNode* underp, Determ determ,
8701 : : Stage stage, bool integralOnly = false) {
8702 : : // Coerce child to any data type; child is self-determined
8703 : : // i.e. isolated from expected type.
8704 : : // e.g. parentp=CONCAT, underp=lhs in CONCAT(lhs,rhs)
8705 : : UASSERT_OBJ(determ == SELF, parentp, "Bad call");
8706 : : UASSERT_OBJ(stage == FINAL || stage == BOTH, parentp, "Bad call");
8707 : : // underp may change as a result of replacement
8708 : : if (stage & PRELIM) {
8709 : : underp = userIterateSubtreeReturnEdits(underp, WidthVP{SELF, PRELIM}.p());
8710 : : }
8711 : : underp
8712 : : = VN_IS(underp, NodeExpr) ? checkCvtUS(VN_AS(underp, NodeExpr), integralOnly) : underp;
8713 : : AstNodeDType* const expDTypep = underp->dtypep();
8714 : : underp = iterateCheck(parentp, side, underp, SELF, FINAL, expDTypep, EXTEND_EXP);
8715 : : (void)underp; // cppcheck
8716 : : }
8717 : : void iterateCheckSizedSelf(AstNode* parentp, const char* side, AstNode* underp, Determ determ,
8718 : : Stage stage) {
8719 : : // Coerce child to any sized-number data type; child is self-determined
8720 : : // i.e. isolated from expected type.
8721 : : // e.g. parentp=CONCAT, underp=lhs in CONCAT(lhs,rhs)
8722 : : UASSERT_OBJ(determ == SELF, parentp, "Bad call");
8723 : : UASSERT_OBJ(stage == FINAL || stage == BOTH, parentp, "Bad call");
8724 : : // underp may change as a result of replacement
8725 : : if (stage & PRELIM) {
8726 : : underp = userIterateSubtreeReturnEdits(underp, WidthVP{SELF, PRELIM}.p());
8727 : : }
8728 : : underp = VN_IS(underp, NodeExpr) ? checkCvtUS(VN_AS(underp, NodeExpr), false) : underp;
8729 : : AstNodeDType* const expDTypep = underp->dtypep();
8730 : : underp = iterateCheck(parentp, side, underp, SELF, FINAL, expDTypep, EXTEND_EXP);
8731 : : AstNodeDType* const checkDtp = expDTypep->skipRefToEnump();
8732 : : if (!checkDtp->isIntegralOrPacked()) {
8733 : : parentp->v3error("Expected numeric type, but got a " << checkDtp->prettyDTypeNameQ()
8734 : : << " data type");
8735 : : }
8736 : : (void)underp; // cppcheck
8737 : : }
8738 : : void iterateCheckAssign(AstNode* parentp, const char* side, AstNode* rhsp, Stage stage,
8739 : : AstNodeDType* lhsDTypep) {
8740 : : // Check using assignment-like context rules
8741 : : // UINFOTREE(1, parentp, "", "checkass");
8742 : : UASSERT_OBJ(stage == FINAL, parentp, "Bad width call");
8743 : : // Create unpacked byte from string see IEEE 1800-2023 5.9
8744 : : if (AstConst* constp = VN_CAST(rhsp, Const)) {
8745 : : if (const AstUnpackArrayDType* const arrayp
8746 : : = VN_CAST(lhsDTypep->skipRefp(), UnpackArrayDType)) {
8747 : : if (AstBasicDType* basicp = VN_CAST(arrayp->subDTypep()->skipRefp(), BasicDType)) {
8748 : : if (basicp->width() == 8 && constp->num().isFromString()) {
8749 : : AstInitArray* newp = new AstInitArray{
8750 : : constp->fileline(), lhsDTypep,
8751 : : new AstConst{constp->fileline(), AstConst::WidthedValue{}, 8, 0}};
8752 : : for (int aindex = arrayp->lo(); aindex <= arrayp->hi(); ++aindex) {
8753 : : int cindex = arrayp->declRange().ascending() ? (arrayp->hi() - aindex)
8754 : : : (aindex - arrayp->lo());
8755 : : V3Number selected{constp, 8};
8756 : : selected.opSel(constp->num(), cindex * 8 + 7, cindex * 8);
8757 : : UINFO(0, " aindex=" << aindex << " cindex=" << cindex
8758 : : << " c=" << selected);
8759 : : if (!selected.isFourState()) {
8760 : : if (const uint32_t c = selected.toUInt()) {
8761 : : newp->addIndexValuep(
8762 : : aindex, new AstConst{constp->fileline(),
8763 : : AstConst::WidthedValue{}, 8, c});
8764 : : }
8765 : : }
8766 : : }
8767 : : UINFO(6, " unpackFromString: " << parentp);
8768 : : rhsp->replaceWith(newp);
8769 : : VL_DO_DANGLING(pushDeletep(rhsp), rhsp);
8770 : : rhsp = newp;
8771 : : }
8772 : : }
8773 : : }
8774 : : }
8775 : : // We iterate and size the RHS based on the result of RHS evaluation
8776 : : checkClassAssign(parentp, side, rhsp, lhsDTypep);
8777 : : const bool lhsStream = (VN_IS(parentp, NodeAssign)
8778 : : && VN_IS(VN_AS(parentp, NodeAssign)->lhsp(), NodeStream));
8779 : : rhsp = iterateCheck(parentp, side, rhsp, ASSIGN, FINAL, lhsDTypep,
8780 : : lhsStream ? EXTEND_OFF : EXTEND_LHS);
8781 : : // UINFOTREE(1, parentp, "", "checkout");
8782 : : (void)rhsp; // cppcheck
8783 : : }
8784 : :
8785 : : void iterateCheckBool(AstNode* parentp, const char* side, AstNode* underp, Stage stage) {
8786 : : UASSERT_OBJ(stage == BOTH, parentp,
8787 : : "Bad call"); // Booleans always self-determined so do BOTH at once
8788 : : // Underp is used in a self-determined but boolean context, reduce a
8789 : : // multibit number to one bit
8790 : : // stage is always BOTH so not passed as argument
8791 : : // underp may change as a result of replacement
8792 : : UASSERT_OBJ(underp, parentp, "Node has no child");
8793 : : underp = userIterateSubtreeReturnEdits(underp, WidthVP{SELF, BOTH}.p());
8794 : : UASSERT_OBJ(underp, parentp, "Node has no child");
8795 : : UASSERT_OBJ(underp->dtypep(), underp,
8796 : : "Node has no type"); // Perhaps forgot to do a prelim visit on it?
8797 : : //
8798 : : // For DOUBLE under a logical op, add implied test against zero, never a warning
8799 : : AstNodeDType* const underVDTypep = underp ? underp->dtypep()->skipRefp() : nullptr;
8800 : : if (underp && underVDTypep->isDouble()) {
8801 : : UINFO(6, " spliceCvtCmpD0: " << underp);
8802 : : VNRelinker linker;
8803 : : underp->unlinkFrBack(&linker);
8804 : : AstNode* const newp
8805 : : = new AstNeqD{parentp->fileline(), VN_AS(underp, NodeExpr),
8806 : : new AstConst{parentp->fileline(), AstConst::RealDouble{}, 0.0}};
8807 : : linker.relink(newp);
8808 : : } else if (VN_IS(underVDTypep, ClassRefDType) || VN_IS(underVDTypep, IfaceRefDType)
8809 : : || (VN_IS(underVDTypep, BasicDType)
8810 : : && VN_AS(underVDTypep, BasicDType)->isCHandle())) {
8811 : : // Allow warning-free "if (handle)"
8812 : : VL_DO_DANGLING(fixWidthReduce(VN_AS(underp, NodeExpr)), underp); // Changed
8813 : : } else if (!underVDTypep->basicp()) {
8814 : : parentp->v3error("Logical operator " << parentp->prettyTypeName()
8815 : : << " expects a non-complex data type on the "
8816 : : << side << ".");
8817 : : underp->replaceWith(new AstConst{parentp->fileline(), AstConst::BitFalse{}});
8818 : : VL_DO_DANGLING(pushDeletep(underp), underp);
8819 : : } else {
8820 : : const bool bad = widthBad(underp, parentp->findBitDType());
8821 : : if (bad) {
8822 : : { // if (warnOn), but not needed here
8823 : : UINFOTREE(5, parentp->backp(), "", "back");
8824 : : parentp->v3widthWarn(1, underp->width(),
8825 : : "Logical operator "
8826 : : << parentp->prettyTypeName()
8827 : : << " expects 1 bit on the " << side << ", but "
8828 : : << side << "'s " << underp->prettyTypeName()
8829 : : << " generates " << underp->width()
8830 : : << (underp->width() != underp->widthMin()
8831 : : ? " or " + cvtToStr(underp->widthMin())
8832 : : : "")
8833 : : << " bits.");
8834 : : }
8835 : : VL_DO_DANGLING(fixWidthReduce(VN_AS(underp, NodeExpr)), underp); // Changed
8836 : : }
8837 : : }
8838 : : }
8839 : :
8840 : : AstNode* iterateCheck(AstNode* parentp, const char* side, AstNode* underp, Determ determ,
8841 : : Stage stage, AstNodeDType* expDTypep, ExtendRule extendRule,
8842 : : bool warnOn = true) {
8843 : : // Perform data type check on underp, which is underneath parentp used for error reporting
8844 : : // Returns the new underp
8845 : : // Conversion to/from doubles and integers are before iterating.
8846 : : UASSERT_OBJ(stage == FINAL, parentp, "Bad state to iterateCheck");
8847 : : UASSERT_OBJ(underp, parentp, "Node has no child");
8848 : : UASSERT_OBJ(underp->dtypep(), underp,
8849 : : "Node has no type"); // Perhaps forgot to do a prelim visit on it?
8850 : : if (VN_IS(underp, NodeDType)) { // Note the node itself, not node's data type
8851 : : // Must be near top of these checks as underp->dtypep() will look normal
8852 : : underp->v3error(ucfirst(parentp->prettyOperatorName())
8853 : : << " expected non-datatype " << side << " but "
8854 : : << underp->prettyNameQ() << " is a datatype.");
8855 : : } else if (expDTypep == underp->dtypep()) { // Perfect
8856 : : underp = userIterateSubtreeReturnEdits(underp, WidthVP{expDTypep, FINAL}.p());
8857 : : } else if (expDTypep->isDouble() && underp->isDouble()) { // Also good
8858 : : underp = userIterateSubtreeReturnEdits(underp, WidthVP{expDTypep, FINAL}.p());
8859 : : } else if (expDTypep->isDouble() && !underp->isDouble()) {
8860 : : AstNode* const oldp
8861 : : = underp; // Need FINAL on children; otherwise splice would block it
8862 : : spliceCvtD(VN_AS(underp, NodeExpr));
8863 : : underp = userIterateSubtreeReturnEdits(oldp, WidthVP{SELF, FINAL}.p());
8864 : : } else if (!expDTypep->isDouble() && underp->isDouble()) {
8865 : : AstNode* const oldp
8866 : : = underp; // Need FINAL on children; otherwise splice would block it
8867 : : spliceCvtS(VN_AS(underp, NodeExpr), true, expDTypep->width()); // Round RHS
8868 : : underp = userIterateSubtreeReturnEdits(oldp, WidthVP{SELF, FINAL}.p());
8869 : : } else if (expDTypep->isString() && !underp->dtypep()->isString()) {
8870 : : AstNode* const oldp
8871 : : = underp; // Need FINAL on children; otherwise splice would block it
8872 : : spliceCvtString(VN_AS(underp, NodeExpr));
8873 : : underp = userIterateSubtreeReturnEdits(oldp, WidthVP{SELF, FINAL}.p());
8874 : : } else {
8875 : : const AstBasicDType* const expBasicp = expDTypep->basicp();
8876 : : const AstBasicDType* const underBasicp = underp->dtypep()->basicp();
8877 : : if (expBasicp && underBasicp) {
8878 : : if (const AstEnumDType* const expEnump
8879 : : = VN_CAST(expDTypep->skipRefToEnump(), EnumDType)) {
8880 : : const auto castable
8881 : : = AstNode::computeCastable(expEnump, underp->dtypep(), underp);
8882 : : if (castable != VCastable::SAMEISH && castable != VCastable::COMPATIBLE
8883 : : && castable != VCastable::ENUM_IMPLICIT && !VN_IS(underp, Cast)
8884 : : && !VN_IS(underp, CastDynamic) && !m_enumItemp
8885 : : && !parentp->fileline()->warnIsOff(V3ErrorCode::ENUMVALUE) && warnOn) {
8886 : : underp->v3warn(ENUMVALUE,
8887 : : "Implicit conversion to enum "
8888 : : << expDTypep->prettyDTypeNameQ() << " from "
8889 : : << underp->dtypep()->prettyDTypeNameQ()
8890 : : << " (IEEE 1800-2023 6.19.3)\n"
8891 : : << parentp->warnMore()
8892 : : << "... Suggest use enum's mnemonic, or static cast");
8893 : : // UINFOTREE(1, parentp->backp(), "", "back");
8894 : : }
8895 : : }
8896 : : AstNodeDType* subDTypep = expDTypep;
8897 : : // We then iterate FINAL before width fixes, as if the under-operation
8898 : : // is e.g. an ADD, the ADD will auto-adjust to the proper data type
8899 : : // or if another operation e.g. ATOI will not.
8900 : : if (determ == SELF) {
8901 : : underp = userIterateSubtreeReturnEdits(underp, WidthVP{SELF, FINAL}.p());
8902 : : } else if (determ == ASSIGN) {
8903 : : // IEEE: Signedness is solely determined by the RHS
8904 : : // (underp), not by the LHS (expDTypep)
8905 : : if (underp->isSigned() != subDTypep->isSigned()
8906 : : || underp->width() != subDTypep->width()) {
8907 : : subDTypep = parentp->findLogicDType(
8908 : : std::max(subDTypep->width(), underp->width()),
8909 : : std::max(subDTypep->widthMin(), underp->widthMin()),
8910 : : VSigning::fromBool(underp->isSigned()));
8911 : : UINFO(9, "Assignment of opposite-signed RHS to LHS: " << parentp);
8912 : : }
8913 : : underp = userIterateSubtreeReturnEdits(underp, WidthVP{subDTypep, FINAL}.p());
8914 : : } else {
8915 : : underp = userIterateSubtreeReturnEdits(underp, WidthVP{subDTypep, FINAL}.p());
8916 : : }
8917 : : // Note the check uses the expected size, not the child's subDTypep as we want the
8918 : : // child node's width to end up correct for the assignment (etc)
8919 : : widthCheckSized(parentp, side, VN_AS(underp, NodeExpr), expDTypep, extendRule,
8920 : : warnOn);
8921 : : } else if (!VN_IS(parentp, Eq) && !VN_IS(parentp, Neq)
8922 : : && !VN_IS(expDTypep->skipRefp(), IfaceRefDType)
8923 : : && VN_IS(underp->dtypep()->skipRefp(), IfaceRefDType)) {
8924 : : underp->v3error(ucfirst(parentp->prettyOperatorName())
8925 : : << " expected non-interface on " << side << " but "
8926 : : << underp->prettyNameQ() << " is an interface.");
8927 : : } else if (const AstIfaceRefDType* expIfaceRefp
8928 : : = VN_CAST(expDTypep->skipRefp(), IfaceRefDType)) {
8929 : : const AstIfaceRefDType* underIfaceRefp
8930 : : = VN_CAST(underp->dtypep()->skipRefp(), IfaceRefDType);
8931 : : if (VN_IS(underp, Const) && VN_AS(underp, Const)->num().isNull()) {
8932 : : // '= null' is ok
8933 : : } else if (!underIfaceRefp) {
8934 : : underp->v3error(ucfirst(parentp->prettyOperatorName())
8935 : : << " expected " << expIfaceRefp->ifaceViaCellp()->prettyNameQ()
8936 : : << " interface on " << side << " but " << underp->prettyNameQ()
8937 : : << " is not an interface.");
8938 : : } else if (expIfaceRefp->ifaceViaCellp() != underIfaceRefp->ifaceViaCellp()) {
8939 : : underp->v3error(ucfirst(parentp->prettyOperatorName())
8940 : : << " expected " << expIfaceRefp->ifaceViaCellp()->prettyNameQ()
8941 : : << " interface on " << side << " but " << underp->prettyNameQ()
8942 : : << " is a different interface ("
8943 : : << underIfaceRefp->ifaceViaCellp()->prettyNameQ() << ").");
8944 : : } else if (underIfaceRefp->modportp()
8945 : : && expIfaceRefp->modportp() != underIfaceRefp->modportp()) {
8946 : : underp->v3error(ucfirst(parentp->prettyOperatorName())
8947 : : << " expected "
8948 : : << (expIfaceRefp->modportp()
8949 : : ? expIfaceRefp->modportp()->prettyNameQ()
8950 : : : "no")
8951 : : << " interface modport on " << side << " but got "
8952 : : << underIfaceRefp->modportp()->prettyNameQ() << " modport.");
8953 : : } else {
8954 : : underp = userIterateSubtreeReturnEdits(underp, WidthVP{expDTypep, FINAL}.p());
8955 : : }
8956 : : } else {
8957 : : // Hope it just works out (perhaps a cast will deal with it)
8958 : : underp = userIterateSubtreeReturnEdits(underp, WidthVP{expDTypep, FINAL}.p());
8959 : : }
8960 : : }
8961 : : return underp;
8962 : : }
8963 : :
8964 : : void
8965 : : widthCheckSized(AstNode* parentp, const char* side,
8966 : : AstNodeExpr* underp, // Node to be checked or have typecast added in front of
8967 : : AstNodeDType* expDTypep, ExtendRule extendRule, bool warnOn = true) {
8968 : : // Issue warnings on sized number width mismatches, then do appropriate size extension
8969 : : // Generally iterateCheck is what is wanted instead of this
8970 : : // UINFO(9,"wchk "<<side<<endl<<" "<<parentp<<endl<<" "<<underp<<endl<<"
8971 : : // e="<<expDTypep<<" i"<<warnOn<<endl);
8972 : : const AstBasicDType* const expBasicp = expDTypep->basicp();
8973 : : const AstBasicDType* const underBasicp = underp->dtypep()->basicp();
8974 : : if (expDTypep == underp->dtypep()) {
8975 : : return; // Same type must match
8976 : : } else if (!expBasicp || expBasicp->isDouble() || !underBasicp
8977 : : || underBasicp->isDouble()) {
8978 : : // This is perhaps a v3fatalSrc as we should have checked the types
8979 : : // before calling widthCheck, but we may have missed a non-sized
8980 : : // check in earlier code, so might as well assume it is the users'
8981 : : // fault.
8982 : : parentp->v3error(ucfirst(parentp->prettyOperatorName())
8983 : : << " expected non-complex non-double " << side << " in width check");
8984 : : #if VL_DEBUG
8985 : : parentp->v3fatalSrc("widthCheckSized should not be called on doubles/complex types");
8986 : : #endif
8987 : : return;
8988 : : } else {
8989 : : const int expWidth = expDTypep->width();
8990 : : int expWidthMin = expDTypep->widthMin();
8991 : : if (expWidthMin == 0) expWidthMin = expWidth;
8992 : : const bool bad = widthBad(underp, expDTypep);
8993 : : if ((bad || underp->width() != expWidth) && fixAutoExtend(underp /*ref*/, expWidth)) {
8994 : : underp = nullptr; // Changes underp
8995 : : return;
8996 : : }
8997 : : // If user has a sizing cast, assume they know what they are doing
8998 : : // (for better or worse)
8999 : : if (VN_IS(parentp->backp(), CastSize)) warnOn = false;
9000 : : if (VN_IS(underp, Const) && VN_AS(underp, Const)->num().isFromString()
9001 : : && expWidth > underp->width()
9002 : : && (((expWidth - underp->width()) % 8) == 0)) { // At least it's character sized
9003 : : // reg [31:0] == "foo" we'll consider probably fine.
9004 : : // Maybe this should be a special warning? Not for now.
9005 : : warnOn = false;
9006 : : }
9007 : : if ((VN_IS(parentp, Add) && underp->width() == 1 && underp->isOne())
9008 : : || (VN_IS(parentp, Sub) && underp->width() == 1 && underp->isOne()
9009 : : && 0 == std::strcmp(side, "RHS"))) {
9010 : : // "foo + 1'b1", or "foo - 1'b1" are very common, people assume
9011 : : // they extend correctly
9012 : : warnOn = false;
9013 : : }
9014 : : if (bad && warnOn) {
9015 : : UINFOTREE(5, parentp->backp(), "", "back");
9016 : :
9017 : : parentp->v3widthWarn(
9018 : : expWidth, underp->width(),
9019 : : ucfirst(parentp->prettyOperatorName())
9020 : : << " expects " << expWidth
9021 : : << (expWidth != expWidthMin ? " or " + cvtToStr(expWidthMin) : "")
9022 : : << " bits on the " << side << ", but " << side << "'s "
9023 : : << underp->prettyTypeName() << " generates " << underp->width()
9024 : : << (underp->width() != underp->widthMin()
9025 : : ? " or " + cvtToStr(underp->widthMin())
9026 : : : "")
9027 : : << " bits.");
9028 : : }
9029 : : if (bad || underp->width() != expWidth) {
9030 : : // If we're in an NodeAssign, don't truncate the RHS if the LHS is
9031 : : // a NodeStream. The streaming operator changes the rules regarding
9032 : : // which bits to truncate.
9033 : : const AstNodeAssign* assignp = VN_CAST(parentp, NodeAssign);
9034 : : const AstPin* pinp = VN_CAST(parentp, Pin);
9035 : : if (assignp && VN_IS(assignp->lhsp(), NodeStream)) {
9036 : : } else if (pinp && pinp->modVarp()->direction() != VDirection::INPUT) {
9037 : : // V3Inst::pinReconnectSimple must deal
9038 : : UINFO(5, "pinInSizeMismatch: " << pinp);
9039 : : } else if (assignp && VN_IS(underp, NodeStream) && expWidth > underp->width()) {
9040 : : // IEEE 1800-2023 11.4.14: When assigning a stream to a wider fixed-size
9041 : : // target, widen by filling zero bits on the right. That is, left-justify
9042 : : // the stream bits within the target width.
9043 : : // Build: ShiftL(Extend(stream, expWidth), expWidth - streamWidth)
9044 : : UINFO(5, "Widen NodeStream RHS with left-justify per 11.4.14");
9045 : : VNRelinker linker;
9046 : : const int shift = expWidth - underp->width();
9047 : : underp->unlinkFrBack(&linker);
9048 : : AstExtend* const widenedp = new AstExtend{underp->fileline(), underp};
9049 : : widenedp->didWidth(true);
9050 : : // Shift left so zeros fill on the right
9051 : : AstNodeExpr* const shiftedp = new AstShiftL{
9052 : : underp->fileline(), widenedp,
9053 : : new AstConst{underp->fileline(), static_cast<uint32_t>(shift)}, expWidth};
9054 : : // Final dtype should match expected
9055 : : shiftedp->dtypep(expDTypep);
9056 : : linker.relink(shiftedp);
9057 : : } else {
9058 : : VL_DO_DANGLING(fixWidthExtend(underp, expDTypep, extendRule), underp);
9059 : : }
9060 : : }
9061 : : }
9062 : : }
9063 : :
9064 : : //----------------------------------------------------------------------
9065 : : // SIGNED/DOUBLE METHODS
9066 : :
9067 : : AstNodeExpr* checkCvtUS(AstNodeExpr* nodep, bool fatal) {
9068 : : if (nodep && nodep->dtypep()->skipRefp()->isDouble()) {
9069 : : if (fatal) {
9070 : : nodep->v3error("Expected integral input to " << nodep->backp()->prettyTypeName());
9071 : : } else {
9072 : : nodep->v3warn(REALCVT,
9073 : : "Implicit conversion of real to integer; expected integral input to "
9074 : : << nodep->backp()->prettyTypeName());
9075 : : }
9076 : : nodep = spliceCvtS(nodep, false, 32);
9077 : : }
9078 : : return nodep;
9079 : : }
9080 : :
9081 : : AstNodeExpr* spliceCvtD(AstNodeExpr* nodep) {
9082 : : // For integer used in REAL context, convert to real
9083 : : // We don't warn here, "2.0 * 2" is common and reasonable
9084 : : if (nodep && !nodep->dtypep()->skipRefp()->isDouble()) {
9085 : : UINFO(6, " spliceCvtD: " << nodep);
9086 : : VNRelinker linker;
9087 : : nodep->unlinkFrBack(&linker);
9088 : : AstNodeExpr* newp;
9089 : : if (nodep->dtypep()->skipRefp()->isSigned()) {
9090 : : newp = new AstISToRD{nodep->fileline(), nodep};
9091 : : } else {
9092 : : newp = new AstIToRD{nodep->fileline(), nodep};
9093 : : }
9094 : : linker.relink(newp);
9095 : : return newp;
9096 : : } else {
9097 : : return nodep;
9098 : : }
9099 : : }
9100 : : AstNodeExpr* spliceCvtS(AstNodeExpr* nodep, bool warnOn, int width) {
9101 : : // IEEE-2012 11.8.1: Signed: Type coercion creates signed
9102 : : // 11.8.2: Argument to convert is self-determined
9103 : : if (nodep && nodep->dtypep()->skipRefp()->isDouble()) {
9104 : : UINFO(6, " spliceCvtS: " << nodep);
9105 : : VNRelinker linker;
9106 : : nodep->unlinkFrBack(&linker);
9107 : : if (const AstConst* const constp = VN_CAST(nodep, Const)) {
9108 : : // We convert to/from int32_t rather than use floor() as want to make sure is
9109 : : // representable in integer's number of bits
9110 : : if (constp->isDouble() && V3Number::epsilonIntegral(constp->num().toDouble())) {
9111 : : warnOn = false;
9112 : : }
9113 : : }
9114 : : if (warnOn) nodep->v3warn(REALCVT, "Implicit conversion of real to integer");
9115 : : AstNodeExpr* const newp = new AstRToIRoundS{nodep->fileline(), nodep};
9116 : : linker.relink(newp);
9117 : : newp->dtypeSetBitSized(width, VSigning::SIGNED);
9118 : : return newp;
9119 : : } else {
9120 : : return nodep;
9121 : : }
9122 : : }
9123 : : AstNodeExpr* spliceCvtString(AstNodeExpr* nodep) {
9124 : : // IEEE-2012 11.8.1: Signed: Type coercion creates signed
9125 : : // 11.8.2: Argument to convert is self-determined
9126 : : if (nodep && !(nodep->dtypep()->basicp() && nodep->dtypep()->basicp()->isString())) {
9127 : : UINFO(6, " spliceCvtString: " << nodep);
9128 : : VNRelinker linker;
9129 : : nodep->unlinkFrBack(&linker);
9130 : : AstNodeExpr* const newp = new AstCvtPackString{nodep->fileline(), nodep};
9131 : : linker.relink(newp);
9132 : : return newp;
9133 : : } else {
9134 : : return nodep;
9135 : : }
9136 : : }
9137 : : AstNodeBiop* replaceWithUOrSVersion(AstNodeBiop* nodep, bool signedFlavorNeeded) {
9138 : : // Given a signed/unsigned node type, create the opposite type
9139 : : // Return new node or nullptr if nothing
9140 : : if (signedFlavorNeeded == nodep->signedFlavor()) return nullptr;
9141 : : if (!nodep->dtypep()) nodep->dtypeFrom(nodep->lhsp());
9142 : : // To simplify callers, some node types don't need to change
9143 : : switch (nodep->type()) {
9144 : : case VNType::Eq: nodep->dtypeChgSigned(signedFlavorNeeded); return nullptr;
9145 : : case VNType::Neq: nodep->dtypeChgSigned(signedFlavorNeeded); return nullptr;
9146 : : case VNType::EqCase: nodep->dtypeChgSigned(signedFlavorNeeded); return nullptr;
9147 : : case VNType::NeqCase: nodep->dtypeChgSigned(signedFlavorNeeded); return nullptr;
9148 : : case VNType::EqWild: nodep->dtypeChgSigned(signedFlavorNeeded); return nullptr;
9149 : : case VNType::NeqWild: nodep->dtypeChgSigned(signedFlavorNeeded); return nullptr;
9150 : : case VNType::Add: nodep->dtypeChgSigned(signedFlavorNeeded); return nullptr;
9151 : : case VNType::Sub: nodep->dtypeChgSigned(signedFlavorNeeded); return nullptr;
9152 : : case VNType::ShiftL: nodep->dtypeChgSigned(signedFlavorNeeded); return nullptr;
9153 : : default: break;
9154 : : }
9155 : : FileLine* const fl = nodep->fileline();
9156 : : AstNodeExpr* const lhsp = nodep->lhsp()->unlinkFrBack();
9157 : : AstNodeExpr* const rhsp = nodep->rhsp()->unlinkFrBack();
9158 : : AstNodeBiop* newp = nullptr;
9159 : : switch (nodep->type()) {
9160 : : case VNType::Gt: newp = new AstGtS{fl, lhsp, rhsp}; break;
9161 : : case VNType::GtS: newp = new AstGt{fl, lhsp, rhsp}; break;
9162 : : case VNType::Gte: newp = new AstGteS{fl, lhsp, rhsp}; break;
9163 : : case VNType::GteS: newp = new AstGte{fl, lhsp, rhsp}; break;
9164 : : case VNType::Lt: newp = new AstLtS{fl, lhsp, rhsp}; break;
9165 : : case VNType::LtS: newp = new AstLt{fl, lhsp, rhsp}; break;
9166 : : case VNType::Lte: newp = new AstLteS{fl, lhsp, rhsp}; break;
9167 : : case VNType::LteS: newp = new AstLte{fl, lhsp, rhsp}; break;
9168 : : case VNType::Div: newp = new AstDivS{fl, lhsp, rhsp}; break;
9169 : : case VNType::DivS: newp = new AstDiv{fl, lhsp, rhsp}; break;
9170 : : case VNType::ModDiv: newp = new AstModDivS{fl, lhsp, rhsp}; break;
9171 : : case VNType::ModDivS: newp = new AstModDiv{fl, lhsp, rhsp}; break;
9172 : : case VNType::Mul: newp = new AstMulS{fl, lhsp, rhsp}; break;
9173 : : case VNType::MulS: newp = new AstMul{fl, lhsp, rhsp}; break;
9174 : : case VNType::ShiftR: newp = new AstShiftRS{fl, lhsp, rhsp}; break;
9175 : : case VNType::ShiftRS: newp = new AstShiftR{fl, lhsp, rhsp}; break;
9176 : : default: // LCOV_EXCL_LINE
9177 : : nodep->v3fatalSrc("Node needs sign change, but bad case: " << nodep);
9178 : : break;
9179 : : }
9180 : : UINFO(6, " ReplaceWithUOrSVersion: " << nodep << " w/ " << newp);
9181 : : nodep->replaceWithKeepDType(newp);
9182 : : VL_DO_DANGLING(pushDeletep(nodep), nodep);
9183 : : return newp;
9184 : : }
9185 : : AstNodeBiop* replaceWithDVersion(AstNodeBiop* nodep) {
9186 : : // Given a signed/unsigned node type, create the opposite type
9187 : : // Return new node or nullptr if nothing
9188 : : if (nodep->doubleFlavor()) return nullptr;
9189 : : FileLine* const fl = nodep->fileline();
9190 : : AstNodeExpr* const lhsp = nodep->lhsp()->unlinkFrBack();
9191 : : AstNodeExpr* const rhsp = nodep->rhsp()->unlinkFrBack();
9192 : : AstNodeBiop* newp = nullptr;
9193 : : // No width change on output;... // All below have bool or double outputs
9194 : : switch (nodep->type()) {
9195 : : case VNType::Add: newp = new AstAddD{fl, lhsp, rhsp}; break;
9196 : : case VNType::Sub: newp = new AstSubD{fl, lhsp, rhsp}; break;
9197 : : case VNType::Pow: newp = new AstPowD{fl, lhsp, rhsp}; break;
9198 : : case VNType::Eq:
9199 : : case VNType::EqCase: newp = new AstEqD{fl, lhsp, rhsp}; break;
9200 : : case VNType::Neq:
9201 : : case VNType::NeqCase: newp = new AstNeqD{fl, lhsp, rhsp}; break;
9202 : : case VNType::Gt:
9203 : : case VNType::GtS: newp = new AstGtD{fl, lhsp, rhsp}; break;
9204 : : case VNType::Gte:
9205 : : case VNType::GteS: newp = new AstGteD{fl, lhsp, rhsp}; break;
9206 : : case VNType::Lt:
9207 : : case VNType::LtS: newp = new AstLtD{fl, lhsp, rhsp}; break;
9208 : : case VNType::Lte:
9209 : : case VNType::LteS: newp = new AstLteD{fl, lhsp, rhsp}; break;
9210 : : case VNType::Div:
9211 : : case VNType::DivS: newp = new AstDivD{fl, lhsp, rhsp}; break;
9212 : : case VNType::Mul:
9213 : : case VNType::MulS: newp = new AstMulD{fl, lhsp, rhsp}; break;
9214 : : default: // LCOV_EXCL_LINE
9215 : : nodep->v3fatalSrc("Node needs conversion to double, but bad case: " << nodep);
9216 : : break;
9217 : : }
9218 : : UINFO(6, " ReplaceWithDVersion: " << nodep << " w/ " << newp);
9219 : : nodep->replaceWith(newp);
9220 : : // No width change; the default created type (bool or double) is correct
9221 : : VL_DO_DANGLING(pushDeletep(nodep), nodep);
9222 : : return newp;
9223 : : }
9224 : : AstNodeBiop* replaceWithNVersion(AstNodeBiop* nodep) {
9225 : : // Given a signed/unsigned node type, replace with string version
9226 : : // Return new node or nullptr if nothing
9227 : : if (nodep->stringFlavor()) return nullptr;
9228 : : FileLine* const fl = nodep->fileline();
9229 : : AstNodeExpr* const lhsp = nodep->lhsp()->unlinkFrBack();
9230 : : AstNodeExpr* const rhsp = nodep->rhsp()->unlinkFrBack();
9231 : : AstNodeBiop* newp = nullptr;
9232 : : // No width change on output;... // All below have bool or double outputs
9233 : : switch (nodep->type()) {
9234 : : case VNType::Eq:
9235 : : case VNType::EqCase: newp = new AstEqN{fl, lhsp, rhsp}; break;
9236 : : case VNType::Neq:
9237 : : case VNType::NeqCase: newp = new AstNeqN{fl, lhsp, rhsp}; break;
9238 : : case VNType::Gt:
9239 : : case VNType::GtS: newp = new AstGtN{fl, lhsp, rhsp}; break;
9240 : : case VNType::Gte:
9241 : : case VNType::GteS: newp = new AstGteN{fl, lhsp, rhsp}; break;
9242 : : case VNType::Lt:
9243 : : case VNType::LtS: newp = new AstLtN{fl, lhsp, rhsp}; break;
9244 : : case VNType::Lte:
9245 : : case VNType::LteS: newp = new AstLteN{fl, lhsp, rhsp}; break;
9246 : : default: // LCOV_EXCL_LINE
9247 : : nodep->v3fatalSrc("Node needs conversion to string, but bad case: " << nodep);
9248 : : break;
9249 : : }
9250 : : UINFO(6, " ReplaceWithNVersion: " << nodep << " w/ " << newp);
9251 : : nodep->replaceWith(newp);
9252 : : // No width change; the default created type (bool or string) is correct
9253 : : VL_DO_DANGLING(pushDeletep(nodep), nodep);
9254 : : return newp;
9255 : : }
9256 : : AstNodeUniop* replaceWithDVersion(AstNodeUniop* nodep) {
9257 : : // Given a signed/unsigned node type, create the opposite type
9258 : : // Return new node or nullptr if nothing
9259 : : if (nodep->doubleFlavor()) return nullptr;
9260 : : FileLine* const fl = nodep->fileline();
9261 : : AstNodeExpr* const lhsp = nodep->lhsp()->unlinkFrBack();
9262 : : AstNodeUniop* newp = nullptr;
9263 : : switch (nodep->type()) {
9264 : : case VNType::Negate: newp = new AstNegateD{fl, lhsp}; break;
9265 : : default: // LCOV_EXCL_LINE
9266 : : nodep->v3fatalSrc("Node needs conversion to double, but bad case: " << nodep);
9267 : : break;
9268 : : }
9269 : : UINFO(6, " ReplaceWithDVersion: " << nodep << " w/ " << newp);
9270 : : nodep->replaceWithKeepDType(newp);
9271 : : VL_DO_DANGLING(pushDeletep(nodep), nodep);
9272 : : return newp;
9273 : : }
9274 : :
9275 : : //----------------------------------------------------------------------
9276 : : // METHODS - strings
9277 : :
9278 : : void replaceWithSFormat(AstMethodCall* nodep, const string& format) {
9279 : : // For string.itoa and similar, replace with SFormatF
9280 : : AstArg* const argp = nodep->argsp();
9281 : : UASSERT_OBJ(argp, nodep,
9282 : : "Argument needed for string method, call methodOkArguments before here");
9283 : : AstNodeVarRef* const fromp = VN_AS(nodep->fromp()->unlinkFrBack(), VarRef);
9284 : : AstNode* const newp = new AstAssign{
9285 : : nodep->fileline(), fromp,
9286 : : new AstSFormatF{nodep->fileline(), format, false, argp->exprp()->unlinkFrBack()}};
9287 : : fromp->access(VAccess::WRITE);
9288 : : pushDeletep(nodep->backp());
9289 : : VL_DO_DANGLING(nodep->backp()->replaceWith(newp), newp);
9290 : : }
9291 : :
9292 : : //----------------------------------------------------------------------
9293 : : // METHODS - data types
9294 : :
9295 : : AstNodeDType* iterateEditMoveDTypep(AstNode* parentp, AstNodeDType* dtnodep) {
9296 : : UASSERT_OBJ(dtnodep, parentp, "Caller should check for nullptr before computing dtype");
9297 : : // Iterate into a data type to resolve that type.
9298 : : // The data type may either:
9299 : : // 1. Be a child (typically getChildDTypep() returns it)
9300 : : // DTypes at parse time get added as these to some node types
9301 : : // such as AstVars.
9302 : : // This function will move it to global scope (that is #2
9303 : : // will now apply).
9304 : : // 2. Be under the Netlist and pointed to by an Ast member variable
9305 : : // (typically refDTypep() or dtypep() returns it)
9306 : : // so removing/changing a variable won't lose the dtype
9307 : :
9308 : : // Case #1 above applies?
9309 : : const bool child1 = (parentp->getChildDTypep() == dtnodep);
9310 : : const bool child2 = (parentp->getChild2DTypep() == dtnodep);
9311 : : if (child1 || child2) {
9312 : : UINFO(9, "iterateEditMoveDTypep child iterating " << dtnodep);
9313 : : // Iterate, this might edit the dtypes which means dtnodep now lost
9314 : : VL_DO_DANGLING(userIterate(dtnodep, nullptr), dtnodep);
9315 : : // Figure out the new dtnodep, remained a child of parent so find it there
9316 : : dtnodep = child1 ? parentp->getChildDTypep() : parentp->getChild2DTypep();
9317 : : UASSERT_OBJ(dtnodep, parentp, "iterateEditMoveDTypep lost pointer to child");
9318 : : UASSERT_OBJ(dtnodep->didWidth(), parentp,
9319 : : "iterateEditMoveDTypep didn't get width resolution of "
9320 : : << dtnodep->prettyTypeName());
9321 : :
9322 : : // Move to under netlist
9323 : : UINFO(9, "iterateEditMoveDTypep child moving " << dtnodep);
9324 : : dtnodep->unlinkFrBack();
9325 : : v3Global.rootp()->typeTablep()->addTypesp(dtnodep);
9326 : : }
9327 : : if (!dtnodep->didWidth()) {
9328 : : UINFO(9, "iterateEditMoveDTypep pointer iterating " << dtnodep);
9329 : : // See notes in visit(AstBracketArrayDType*)
9330 : : UASSERT_OBJ(!VN_IS(dtnodep, BracketArrayDType), parentp,
9331 : : "Brackets should have been iterated as children");
9332 : : userIterate(dtnodep, nullptr);
9333 : : UASSERT_OBJ(dtnodep->didWidth(), parentp,
9334 : : "iterateEditMoveDTypep didn't get width resolution");
9335 : : }
9336 : : return dtnodep;
9337 : : }
9338 : :
9339 : : AstConst* dimensionValue(FileLine* fileline, AstNodeDType* nodep, VAttrType attrType,
9340 : : int dim) {
9341 : : // Return the dimension value for the specified attribute and constant dimension
9342 : : AstNodeDType* dtypep = nodep->skipRefp();
9343 : : VNumRange declRange; // ranged() set false
9344 : : for (int i = 1; i <= dim; ++i) {
9345 : : // UINFO(9, " dim at "<<dim<<" "<<dtypep);
9346 : : declRange = VNumRange{}; // ranged() set false
9347 : : if (const AstNodeArrayDType* const adtypep = VN_CAST(dtypep, NodeArrayDType)) {
9348 : : declRange = adtypep->declRange();
9349 : : if (i < dim) dtypep = adtypep->subDTypep()->skipRefp();
9350 : : continue;
9351 : : } else if (const AstNodeUOrStructDType* const adtypep
9352 : : = VN_CAST(dtypep, NodeUOrStructDType)) {
9353 : : declRange = adtypep->declRange();
9354 : : break; // Sub elements don't look like arrays and can't iterate into
9355 : : } else if (const AstBasicDType* const adtypep = VN_CAST(dtypep, BasicDType)) {
9356 : : if (adtypep->isRanged()) declRange = adtypep->declRange();
9357 : : break;
9358 : : }
9359 : : break; // LCOV_EXCL_LINE
9360 : : }
9361 : : AstConst* valp = nullptr; // If nullptr, construct from val
9362 : : int val = 0;
9363 : : switch (attrType) {
9364 : : case VAttrType::DIM_BITS: {
9365 : : int bits = 1;
9366 : : while (dtypep) {
9367 : : // UINFO(9, " bits at " << bits << " " << dtypep);
9368 : : if (const AstNodeArrayDType* const adtypep = VN_CAST(dtypep, NodeArrayDType)) {
9369 : : bits *= adtypep->declRange().elements();
9370 : : dtypep = adtypep->subDTypep()->skipRefp();
9371 : : continue;
9372 : : } else if (const AstNodeUOrStructDType* const adtypep
9373 : : = VN_CAST(dtypep, NodeUOrStructDType)) {
9374 : : bits *= adtypep->width();
9375 : : break;
9376 : : } else if (const AstBasicDType* const adtypep = VN_CAST(dtypep, BasicDType)) {
9377 : : bits *= adtypep->width();
9378 : : break;
9379 : : }
9380 : : break;
9381 : : }
9382 : : if (dim == 0) {
9383 : : val = 0;
9384 : : } else if (dim == 1 && !declRange.ranged()
9385 : : && bits == 1) { // $bits should be sane for non-arrays
9386 : : val = nodep->width();
9387 : : } else {
9388 : : val = bits;
9389 : : }
9390 : : break; // LCOV_EXCL_LINE
9391 : : }
9392 : : case VAttrType::DIM_HIGH: val = !declRange.ranged() ? 0 : declRange.hi(); break;
9393 : : case VAttrType::DIM_LEFT: val = !declRange.ranged() ? 0 : declRange.left(); break;
9394 : : case VAttrType::DIM_LOW: val = !declRange.ranged() ? 0 : declRange.lo(); break;
9395 : : case VAttrType::DIM_RIGHT: val = !declRange.ranged() ? 0 : declRange.right(); break;
9396 : : case VAttrType::DIM_INCREMENT:
9397 : : val = (declRange.ranged() && declRange.ascending()) ? -1 : 1;
9398 : : break;
9399 : : case VAttrType::DIM_SIZE: val = !declRange.ranged() ? 0 : declRange.elements(); break;
9400 : : default: nodep->v3fatalSrc("Missing DIM ATTR type case"); break;
9401 : : }
9402 : : if (!valp) valp = new AstConst{fileline, AstConst::Signed32{}, val};
9403 : : UINFO(9, " $dimension " << attrType.ascii() << "(" << cvtToHex(dtypep) << "," << dim
9404 : : << ")=" << valp);
9405 : : return valp;
9406 : : }
9407 : : AstVar* dimensionVarp(AstNodeDType* nodep, VAttrType attrType, uint32_t msbdim) {
9408 : : // Return a variable table which has specified dimension properties for this variable
9409 : : const auto pair = m_tableMap.emplace(std::piecewise_construct, //
9410 : : std::forward_as_tuple(nodep, attrType),
9411 : : std::forward_as_tuple(nullptr));
9412 : : if (pair.second) {
9413 : : AstNodeArrayDType* const vardtypep
9414 : : = new AstUnpackArrayDType{nodep->fileline(), nodep->findSigned32DType(),
9415 : : new AstRange(nodep->fileline(), msbdim, 0)};
9416 : : AstInitArray* const initp = new AstInitArray{nodep->fileline(), vardtypep, nullptr};
9417 : : v3Global.rootp()->typeTablep()->addTypesp(vardtypep);
9418 : : AstVar* const varp = new AstVar{nodep->fileline(), VVarType::MODULETEMP,
9419 : : "__Vdimtab_" + VString::downcase(attrType.ascii())
9420 : : + cvtToStr(m_dtTables++),
9421 : : vardtypep};
9422 : : varp->isConst(true);
9423 : : varp->isStatic(true);
9424 : : varp->valuep(initp);
9425 : : // Add to root, as don't know module we are in, and aids later structure sharing
9426 : : v3Global.rootp()->dollarUnitPkgAddp()->addStmtsp(varp);
9427 : : // Element 0 is a non-index and has speced values
9428 : : initp->addValuep(dimensionValue(nodep->fileline(), nodep, attrType, 0));
9429 : : for (unsigned i = 1; i < msbdim + 1; ++i) {
9430 : : initp->addValuep(dimensionValue(nodep->fileline(), nodep, attrType, i));
9431 : : }
9432 : : userIterate(varp, nullptr); // May have already done $unit so must do this var
9433 : : pair.first->second = varp;
9434 : : }
9435 : : return pair.first->second;
9436 : : }
9437 : : static uint64_t enumMaxValue(const AstNode* errNodep, const AstEnumDType* adtypep) {
9438 : : // Most enums unless overridden are 32 bits, so we size array
9439 : : // based on max enum value used.
9440 : : // Ideally we would have a fast algorithm when a number is
9441 : : // of small width and complete and so can use an array, and
9442 : : // a map for when the value is many bits and sparse.
9443 : : uint64_t maxval = 0;
9444 : : for (const AstEnumItem* itemp = adtypep->itemsp(); itemp;
9445 : : itemp = VN_AS(itemp->nextp(), EnumItem)) {
9446 : : const AstConst* const vconstp = VN_CAST(itemp->valuep(), Const);
9447 : : UASSERT_OBJ(vconstp, errNodep, "Enum item without constified value");
9448 : : if (!vconstp->num().isAnyXZ() && vconstp->toUQuad() >= maxval)
9449 : : maxval = vconstp->toUQuad();
9450 : : }
9451 : : if (adtypep->itemsp()->width() > 64) {
9452 : : errNodep->v3warn(E_UNSUPPORTED,
9453 : : "Unsupported: enum next/prev/name method on enum with > 64 bits");
9454 : : return 64;
9455 : : }
9456 : : return maxval;
9457 : : }
9458 : : static AstVar* enumVarp(AstEnumDType* const nodep, VAttrType attrType, bool assoc,
9459 : : uint32_t msbdim) {
9460 : : // Return a variable table which has specified dimension properties for this variable
9461 : : const auto pair = nodep->tableMap().emplace(attrType, nullptr);
9462 : : if (pair.second) {
9463 : : UINFO(9, "Construct Venumtab attr=" << attrType.ascii() << " assoc=" << assoc
9464 : : << " max=" << msbdim << " for " << nodep);
9465 : : AstNodeDType* basep;
9466 : : if (attrType == VAttrType::ENUM_NAME) {
9467 : : basep = nodep->findStringDType();
9468 : : } else if (attrType == VAttrType::ENUM_VALID) {
9469 : : // TODO in theory we could bit-pack the bits in the table, but
9470 : : // would require additional operations to extract, so only
9471 : : // would be worth it for larger tables which perhaps could be
9472 : : // better handled with equation generation?
9473 : : basep = nodep->findBitDType();
9474 : : } else {
9475 : : basep = nodep->dtypep();
9476 : : }
9477 : : AstNodeDType* vardtypep;
9478 : : if (assoc) {
9479 : : vardtypep = new AstAssocArrayDType{nodep->fileline(), basep, nodep};
9480 : : } else {
9481 : : vardtypep = new AstUnpackArrayDType{nodep->fileline(), basep,
9482 : : new AstRange(nodep->fileline(), msbdim, 0)};
9483 : : }
9484 : : AstInitArray* const initp = new AstInitArray{nodep->fileline(), vardtypep, nullptr};
9485 : : v3Global.rootp()->typeTablep()->addTypesp(vardtypep);
9486 : : AstVar* const varp = new AstVar{nodep->fileline(), VVarType::MODULETEMP,
9487 : : "__Venumtab_" + VString::downcase(attrType.ascii())
9488 : : + cvtToStr(nodep->uniqueNum()),
9489 : : vardtypep};
9490 : : varp->lifetime(VLifetime::STATIC_EXPLICIT);
9491 : : varp->isConst(true);
9492 : : varp->isStatic(true);
9493 : : varp->valuep(initp);
9494 : : // Add to root, as don't know module we are in, and aids later structure sharing
9495 : : v3Global.rootp()->dollarUnitPkgAddp()->addStmtsp(varp);
9496 : :
9497 : : // Default for all unspecified values
9498 : : if (attrType == VAttrType::ENUM_NAME) {
9499 : : initp->defaultp(new AstConst{nodep->fileline(), AstConst::String{}, ""});
9500 : : } else if (attrType == VAttrType::ENUM_NEXT || attrType == VAttrType::ENUM_PREV) {
9501 : : initp->defaultp(
9502 : : new AstConst{nodep->fileline(), V3Number{nodep, nodep->width(), 0}});
9503 : : } else if (attrType == VAttrType::ENUM_VALID) {
9504 : : initp->defaultp(new AstConst{nodep->fileline(), AstConst::BitFalse{}});
9505 : : } else {
9506 : : nodep->v3fatalSrc("Bad case");
9507 : : }
9508 : :
9509 : : // Find valid values and populate
9510 : : UASSERT_OBJ(nodep->itemsp(), nodep, "enum without items");
9511 : : std::map<uint64_t, AstNodeExpr*> values;
9512 : : {
9513 : : AstEnumItem* const firstp = nodep->itemsp();
9514 : : const AstEnumItem* prevp = firstp; // Prev must start with last item
9515 : : while (prevp->nextp()) prevp = VN_AS(prevp->nextp(), EnumItem);
9516 : : for (AstEnumItem* itemp = firstp; itemp;) {
9517 : : AstEnumItem* const nextp = VN_AS(itemp->nextp(), EnumItem);
9518 : : const AstConst* const vconstp = VN_AS(itemp->valuep(), Const);
9519 : : UASSERT_OBJ(vconstp, nodep, "Enum item without constified value");
9520 : : if (!vconstp->num().isAnyXZ()) { // Can 2-state runtime decode
9521 : : const uint64_t i = vconstp->toUQuad();
9522 : : if (attrType == VAttrType::ENUM_NAME) {
9523 : : values[i] = new AstConst{nodep->fileline(), AstConst::String{},
9524 : : itemp->name()};
9525 : : } else if (attrType == VAttrType::ENUM_NEXT) {
9526 : : values[i]
9527 : : = (nextp ? nextp : firstp)->valuep()->cloneTree(false); // A const
9528 : : } else if (attrType == VAttrType::ENUM_PREV) {
9529 : : values[i] = prevp->valuep()->cloneTree(false); // A const
9530 : : } else if (attrType == VAttrType::ENUM_VALID) {
9531 : : values[i] = new AstConst{nodep->fileline(), AstConst::BitTrue{}};
9532 : : } else {
9533 : : nodep->v3fatalSrc("Bad case");
9534 : : }
9535 : : }
9536 : : prevp = itemp;
9537 : : itemp = nextp;
9538 : : }
9539 : : }
9540 : : // Add all specified values to table
9541 : : if (assoc) {
9542 : : for (const auto& itr : values) initp->addIndexValuep(itr.first, itr.second);
9543 : : } else {
9544 : : for (uint64_t i = 0; i < (msbdim + 1); ++i) {
9545 : : if (values[i]) initp->addIndexValuep(i, values[i]);
9546 : : }
9547 : : }
9548 : : varp->didWidth(true); // May have already done $unit so must do this var
9549 : : pair.first->second = varp;
9550 : : }
9551 : : return pair.first->second;
9552 : : }
9553 : :
9554 : : static AstNodeExpr* enumSelect(AstNodeExpr* nodep, AstEnumDType* adtypep, VAttrType attrType) {
9555 : : // Return expression to get given attrType information from a enum's value (nodep)
9556 : : // Need a runtime lookup table. Yuk.
9557 : : const uint64_t msbdim = enumMaxValue(nodep, adtypep);
9558 : : const bool assoc = msbdim > ENUM_LOOKUP_BITS;
9559 : : AstNodeExpr* newp;
9560 : : if (assoc) {
9561 : : AstVar* const varp = enumVarp(adtypep, attrType, true, 0);
9562 : : newp = new AstAssocSel{nodep->fileline(), newVarRefDollarUnit(varp), nodep};
9563 : : } else {
9564 : : const int selwidth = V3Number::log2b(msbdim) + 1; // Width to address a bit
9565 : : AstVar* const varp = enumVarp(adtypep, attrType, false, (1ULL << selwidth) - 1);
9566 : : newp = new AstArraySel{
9567 : : nodep->fileline(), newVarRefDollarUnit(varp),
9568 : : // Select in case widths are off due to msblen!=width
9569 : : // We return "random" values if outside the range, which is fine
9570 : : // as next/previous on illegal values just need something good out
9571 : : new AstSel{nodep->fileline(), nodep, 0, selwidth}};
9572 : : }
9573 : : if (attrType == VAttrType::ENUM_NAME) {
9574 : : newp->dtypeSetString();
9575 : : } else if (attrType == VAttrType::ENUM_VALID) {
9576 : : newp->dtypeSetBit();
9577 : : } else {
9578 : : newp->dtypeFrom(adtypep); // To prevent a later ENUMVALUE
9579 : : }
9580 : : return newp;
9581 : : }
9582 : : static AstNodeExpr* enumTestValid(AstNodeExpr* valp, AstEnumDType* enumDtp) {
9583 : : const uint64_t maxval = enumMaxValue(valp, enumDtp);
9584 : : const bool assoc = maxval > ENUM_LOOKUP_BITS;
9585 : : AstNodeExpr* testp = nullptr;
9586 : : FileLine* const fl_novalue = new FileLine{valp->fileline()};
9587 : : fl_novalue->warnOff(V3ErrorCode::ENUMVALUE, true);
9588 : : fl_novalue->warnOff(V3ErrorCode::CMPCONST, true);
9589 : : if (assoc) {
9590 : : AstVar* const varp = enumVarp(enumDtp, VAttrType::ENUM_VALID, true, 0);
9591 : : testp = new AstAssocSel{fl_novalue, newVarRefDollarUnit(varp),
9592 : : valp->cloneTreePure(false)};
9593 : : } else {
9594 : : const int selwidth = V3Number::log2b(maxval) + 1; // Width to address a bit
9595 : : AstVar* const varp
9596 : : = enumVarp(enumDtp, VAttrType::ENUM_VALID, false, (1ULL << selwidth) - 1);
9597 : : FileLine* const fl_nowidth = new FileLine{fl_novalue};
9598 : : fl_nowidth->warnOff(V3ErrorCode::WIDTH, true);
9599 : : testp = new AstCond{
9600 : : fl_novalue,
9601 : : new AstGt{fl_nowidth, valp->cloneTreePure(false),
9602 : : new AstConst{fl_nowidth, AstConst::Unsized64{}, maxval}},
9603 : : new AstConst{fl_novalue, AstConst::BitFalse{}},
9604 : : new AstArraySel{fl_novalue, newVarRefDollarUnit(varp),
9605 : : new AstSel{fl_novalue, valp->cloneTreePure(false), 0, selwidth}}};
9606 : : }
9607 : : return testp;
9608 : : }
9609 : :
9610 : : PatVecMap patVectorMap(AstPattern* nodep, const VNumRange& range) {
9611 : : PatVecMap patmap;
9612 : : int element = range.left();
9613 : : for (AstPatMember* patp = VN_AS(nodep->itemsp(), PatMember); patp;
9614 : : patp = VN_AS(patp->nextp(), PatMember)) {
9615 : : if (patp->keyp()) {
9616 : : V3Const::constifyParamsNoWarnEdit(patp->keyp());
9617 : : if (patp->varrefp()) V3Const::constifyParamsEdit(patp->varrefp());
9618 : : if (const AstConst* const constp = VN_CAST(patp->keyp(), Const)) {
9619 : : element = constp->toSInt();
9620 : : } else if (const AstConst* const constp = VN_CAST(patp->varrefp(), Const)) {
9621 : : element = constp->toSInt();
9622 : : } else {
9623 : : patp->keyp()->v3error("Assignment pattern key not supported/understood: "
9624 : : << patp->keyp()->prettyTypeName());
9625 : : }
9626 : : }
9627 : : const bool newEntry = patmap.emplace(element, patp).second;
9628 : : if (!newEntry) {
9629 : : patp->v3error("Assignment pattern key used multiple times: " << element);
9630 : : }
9631 : : element += range.leftToRightInc();
9632 : : }
9633 : : return patmap;
9634 : : }
9635 : :
9636 : : void patConcatConvertRecurse(AstPattern* patternp, AstConcat* nodep) {
9637 : : if (AstConcat* lhsp = VN_CAST(nodep->lhsp(), Concat)) {
9638 : : patConcatConvertRecurse(patternp, lhsp);
9639 : : } else {
9640 : : AstPatMember* const newp = new AstPatMember{
9641 : : nodep->lhsp()->fileline(), nodep->lhsp()->unlinkFrBack(), nullptr, nullptr};
9642 : : newp->isConcat(true);
9643 : : patternp->addItemsp(newp);
9644 : : }
9645 : : if (AstConcat* rhsp = VN_CAST(nodep->rhsp(), Concat)) {
9646 : : patConcatConvertRecurse(patternp, rhsp);
9647 : : } else {
9648 : : AstPatMember* const newp = new AstPatMember{
9649 : : nodep->rhsp()->fileline(), nodep->rhsp()->unlinkFrBack(), nullptr, nullptr};
9650 : : newp->isConcat(true);
9651 : : patternp->addItemsp(newp);
9652 : : }
9653 : : }
9654 : :
9655 : : void makeOpenArrayShell(AstNodeFTaskRef* nodep) {
9656 : : UINFO(4, "Replicate openarray function " << nodep->taskp());
9657 : : AstNodeFTask* const oldTaskp = nodep->taskp();
9658 : : oldTaskp->dpiOpenParentInc();
9659 : : UASSERT_OBJ(!oldTaskp->dpiOpenChild(), oldTaskp,
9660 : : "DPI task should be parent or child, not both");
9661 : : AstNodeFTask* const newTaskp = oldTaskp->cloneTree(false);
9662 : : newTaskp->dpiOpenChild(true);
9663 : : newTaskp->dpiOpenParentClear();
9664 : : newTaskp->name(newTaskp->name() + "__Vdpioc" + cvtToStr(oldTaskp->dpiOpenParent()));
9665 : : oldTaskp->addNextHere(newTaskp);
9666 : : // Relink reference to new function
9667 : : nodep->taskp(newTaskp);
9668 : : nodep->name(nodep->taskp()->name());
9669 : : // Replace open array arguments with the callee's task
9670 : : const V3TaskConnects tconnects = V3Task::taskConnects(nodep, nodep->taskp()->stmtsp());
9671 : : for (const auto& tconnect : tconnects) {
9672 : : AstVar* const portp = tconnect.first;
9673 : : const AstArg* const argp = tconnect.second;
9674 : : const AstNode* const pinp = argp->exprp();
9675 : : if (!pinp) continue; // Argument error we'll find later
9676 : : if (hasOpenArrayDTypeRecurse(portp->dtypep())) portp->dtypep(pinp->dtypep());
9677 : : }
9678 : : }
9679 : :
9680 : : bool markHasOpenArray(AstNodeFTask* nodep) {
9681 : : bool hasOpen = false;
9682 : : for (AstNode* stmtp = nodep->stmtsp(); stmtp; stmtp = stmtp->nextp()) {
9683 : : if (AstVar* const portp = VN_CAST(stmtp, Var)) {
9684 : : if (portp->isDpiOpenArray() || hasOpenArrayDTypeRecurse(portp->dtypep())) {
9685 : : portp->isDpiOpenArray(true);
9686 : : hasOpen = true;
9687 : : }
9688 : : }
9689 : : }
9690 : : return hasOpen;
9691 : : }
9692 : : bool hasOpenArrayDTypeRecurse(AstNodeDType* nodep) {
9693 : : // Return true iff this datatype or child has an openarray
9694 : : if (VN_IS(nodep, UnsizedArrayDType)) return true;
9695 : : if (nodep->subDTypep()) return hasOpenArrayDTypeRecurse(nodep->subDTypep()->skipRefp());
9696 : : return false;
9697 : : }
9698 : : AstNodeDType* dtypeNotIntAtomOrVecRecurse(AstNodeDType* nodep, bool ranged = false) {
9699 : : // If node is _not_ integer or atomic, return node that makes it fail
9700 : : nodep = nodep->skipRefToEnump();
9701 : : if (AstBasicDType* const dtp = VN_CAST(nodep, BasicDType)) {
9702 : : if (ranged && (!dtp->isBitLogic() || dtp->isRanged()))
9703 : : return dtp; // Packed when already packed
9704 : : if (dtp->keyword().isIntNumeric()) return nullptr;
9705 : : return dtp;
9706 : : } else if (AstPackArrayDType* const dtp = VN_CAST(nodep, PackArrayDType)) {
9707 : : if (ranged) return dtp; // Packed when already packed
9708 : : return dtypeNotIntAtomOrVecRecurse(nodep->subDTypep(), true);
9709 : : }
9710 : : return nodep;
9711 : : }
9712 : : AstNodeDType* dtypeNot4StateIntegralRecurse(AstNodeDType* nodep) {
9713 : : // If node is _not_ inet valid data type, 4-state integral packed or union, return node
9714 : : // that makes it fail
9715 : : nodep = nodep->skipRefp();
9716 : : if (AstBasicDType* const dtp = VN_CAST(nodep, BasicDType)) {
9717 : : if (!dtp->keyword().isFourstate()) return dtp;
9718 : : return nullptr;
9719 : : } else if (AstNodeArrayDType* const dtp = VN_CAST(nodep, NodeArrayDType)) {
9720 : : return dtypeNot4StateIntegralRecurse(dtp->subDTypep());
9721 : : } else if (AstNodeUOrStructDType* const dtp = VN_CAST(nodep, NodeUOrStructDType)) {
9722 : : for (AstMemberDType* itemp = dtp->membersp(); itemp;
9723 : : itemp = VN_AS(itemp->nextp(), MemberDType)) {
9724 : : AstNodeDType* const badDtp = dtypeNot4StateIntegralRecurse(itemp->dtypep());
9725 : : if (badDtp) return badDtp;
9726 : : }
9727 : : return nullptr;
9728 : : }
9729 : : return nodep;
9730 : : }
9731 : : void checkForceReleaseLhs(AstNode* nodep, AstNode* lhsp) {
9732 : : // V3Force can't check as vector may have expanded, or propagated constant into index
9733 : : if (AstNode* const selNodep = selectNonConstantRecurse(lhsp))
9734 : : nodep->v3error((VN_IS(nodep, Release) ? "Release"s : "Force"s)
9735 : : + " left-hand-side must not have variable bit/part select "
9736 : : "(IEEE 1800-2023 10.6.2)\n"
9737 : : << nodep->warnContextPrimary() << '\n'
9738 : : << selNodep->warnOther() << "... Location of non-constant index\n"
9739 : : << selNodep->warnContextSecondary());
9740 : : }
9741 : : AstNode* selectNonConstantRecurse(AstNode* nodep, bool inSel = false) {
9742 : : // If node has a non-constant select, return that select
9743 : : AstNode* resultp = nullptr;
9744 : : if (AstNodeSel* const anodep = VN_CAST(nodep, NodeSel)) {
9745 : : resultp = selectNonConstantRecurse(anodep->fromp(), inSel);
9746 : : if (resultp) return resultp;
9747 : : resultp = selectNonConstantRecurse(anodep->bitp(), true);
9748 : : } else if (AstSel* const anodep = VN_CAST(nodep, Sel)) {
9749 : : resultp = selectNonConstantRecurse(anodep->fromp(), inSel);
9750 : : if (resultp) return resultp;
9751 : : resultp = selectNonConstantRecurse(anodep->lsbp(), true);
9752 : : } else if (AstNodeVarRef* const anodep = VN_CAST(nodep, NodeVarRef)) {
9753 : : if (inSel && !anodep->varp()->isParam() && !anodep->varp()->isGenVar()) return anodep;
9754 : : } else {
9755 : : if (AstNode* const refp = nodep->op1p())
9756 : : resultp = selectNonConstantRecurse(refp, inSel);
9757 : : if (resultp) return resultp;
9758 : : if (AstNode* const refp = nodep->op2p())
9759 : : resultp = selectNonConstantRecurse(refp, inSel);
9760 : : if (resultp) return resultp;
9761 : : if (AstNode* const refp = nodep->op3p())
9762 : : resultp = selectNonConstantRecurse(refp, inSel);
9763 : : if (resultp) return resultp;
9764 : : if (AstNode* const refp = nodep->op4p())
9765 : : resultp = selectNonConstantRecurse(refp, inSel);
9766 : : }
9767 : : return resultp;
9768 : : }
9769 : :
9770 : : //----------------------------------------------------------------------
9771 : : // METHODS - special type detection
9772 : :
9773 : : void assertAtExpr(AstNode* nodep) {
9774 : : if (VL_UNCOVERABLE(!m_vup)) {
9775 : : nodep->v3fatalSrc("Unexpected '" << nodep->prettyTypeName() << "' expression under '"
9776 : : << nodep->backp()->prettyTypeName() << "'");
9777 : : }
9778 : : }
9779 : : void assertAtStatement(AstNode* nodep) {
9780 : : if (VL_UNCOVERABLE(m_vup && !m_vup->selfDtm())) {
9781 : : UINFO(1, "-: " << m_vup);
9782 : : nodep->v3fatalSrc("No dtype expected at statement " << nodep->prettyTypeName());
9783 : : }
9784 : : }
9785 : : void checkConstantOrReplace(AstNode* nodep, bool noFourState, const string& message) {
9786 : : // See also V3WidthSel::checkConstantOrReplace
9787 : : // Note can't call V3Const::constifyParam(nodep) here, as constify may change nodep on us!
9788 : : AstConst* const constp = VN_CAST(nodep, Const);
9789 : : if (!constp || (noFourState && constp->num().isFourState())) {
9790 : : nodep->v3error(message);
9791 : : nodep->replaceWith(new AstConst{nodep->fileline(), AstConst::Unsized32{}, 1});
9792 : : VL_DO_DANGLING2(pushDeletep(nodep), nodep, constp);
9793 : : return;
9794 : : }
9795 : : }
9796 : : static AstVarRef* newVarRefDollarUnit(AstVar* nodep) {
9797 : : AstVarRef* const varrefp = new AstVarRef{nodep->fileline(), nodep, VAccess::READ};
9798 : : varrefp->classOrPackagep(v3Global.rootp()->dollarUnitPkgAddp());
9799 : : return varrefp;
9800 : : }
9801 : : AstNode* nodeForUnsizedWarning(AstNode* nodep) {
9802 : : // Return a nodep to use for unsized warnings, reporting on child if can
9803 : : if (nodep->op1p() && nodep->op1p()->dtypep() && !nodep->op1p()->dtypep()->widthSized()) {
9804 : : return nodep->op1p();
9805 : : } else if (nodep->op2p() && nodep->op2p()->dtypep()
9806 : : && !nodep->op2p()->dtypep()->widthSized()) {
9807 : : return nodep->op2p();
9808 : : }
9809 : : return nodep; // By default return this
9810 : : }
9811 : : AstRefDType* checkRefToTypedefRecurse(AstNode* nodep, AstTypedef* typedefp) {
9812 : : // Recurse all children looking for self reference
9813 : : // This avoids iterateEditMoveDTypep going into a hard to resolve loop
9814 : : // Only call once for any given typedef, or will become O(n^2)
9815 : : if (VL_LIKELY(!nodep)) return nullptr;
9816 : : if (auto* const refp = VN_CAST(nodep, RefDType)) {
9817 : : if (refp->typedefp() == typedefp) return refp;
9818 : : }
9819 : : if (auto* const refp = checkRefToTypedefRecurse(nodep->op1p(), typedefp)) return refp;
9820 : : if (auto* const refp = checkRefToTypedefRecurse(nodep->op2p(), typedefp)) return refp;
9821 : : if (auto* const refp = checkRefToTypedefRecurse(nodep->op3p(), typedefp)) return refp;
9822 : : if (auto* const refp = checkRefToTypedefRecurse(nodep->op4p(), typedefp)) return refp;
9823 : : return nullptr;
9824 : : }
9825 : :
9826 : : //----------------------------------------------------------------------
9827 : : // METHODS - special iterators
9828 : : // These functions save/restore the AstNUser information so it can pass to child nodes.
9829 : :
9830 : : AstNode* userIterateSubtreeReturnEdits(AstNode* nodep, WidthVP* vup) {
9831 : : if (!nodep) return nullptr;
9832 : : AstNode* ret;
9833 : : {
9834 : : VL_RESTORER(m_vup);
9835 : : m_vup = vup;
9836 : : ret = iterateSubtreeReturnEdits(nodep);
9837 : : }
9838 : : return ret;
9839 : : }
9840 : : void userIterate(AstNode* nodep, WidthVP* vup) {
9841 : : if (!nodep) return;
9842 : : VL_RESTORER(m_vup);
9843 : : m_vup = vup;
9844 : : iterate(nodep);
9845 : : }
9846 : : void userIterateAndNext(AstNode* nodep, WidthVP* vup) {
9847 : : if (!nodep) return;
9848 : : if (nodep->didWidth()) return; // Avoid iterating list we have already iterated
9849 : : VL_RESTORER(m_vup);
9850 : : m_vup = vup;
9851 : : iterateAndNextNull(nodep);
9852 : : }
9853 : : void userIterateChildren(AstNode* nodep, WidthVP* vup) {
9854 : : if (!nodep) return;
9855 : : VL_RESTORER(m_vup);
9856 : : m_vup = vup;
9857 : : iterateChildren(nodep);
9858 : : }
9859 : : void userIterateChildrenBackwardsConst(AstNode* nodep, WidthVP* vup) {
9860 : : if (!nodep) return;
9861 : : VL_RESTORER(m_vup);
9862 : : m_vup = vup;
9863 : : iterateChildrenBackwardsConst(nodep);
9864 : : }
9865 : :
9866 : : public:
9867 : : // CONSTRUCTORS
9868 : : WidthVisitor(bool paramsOnly, // [in] TRUE if we are considering parameters only.
9869 : : bool doGenerate) // [in] TRUE if we are inside a generate statement and
9870 : : // // don't wish to trigger errors
9871 : : : m_insideTempNames{"__VInside"}
9872 : : , m_paramsOnly{paramsOnly}
9873 : : , m_doGenerate{doGenerate} {}
9874 : : AstNode* mainAcceptEdit(AstNode* nodep) {
9875 : : return userIterateSubtreeReturnEdits(nodep, WidthVP{SELF, BOTH}.p());
9876 : : }
9877 : : ~WidthVisitor() override = default;
9878 : : };
9879 : :
9880 : : //######################################################################
9881 : : // Width class functions
9882 : :
9883 : : void V3Width::width(AstNetlist* nodep) {
9884 : : UINFO(2, __FUNCTION__ << ":");
9885 : : {
9886 : : // We should do it in bottom-up module order, but it works in any order.
9887 : : const WidthClearVisitor cvisitor{nodep};
9888 : : WidthVisitor visitor{false, false};
9889 : : (void)visitor.mainAcceptEdit(nodep);
9890 : : WidthRemoveVisitor rvisitor;
9891 : : (void)rvisitor.mainAcceptEdit(nodep);
9892 : : } // Destruct before checking
9893 : : V3Global::dumpCheckGlobalTree("width", 0, dumpTreeEitherLevel() >= 3);
9894 : : }
9895 : : //! Single node parameter propagation
9896 : : //! Smaller step... Only do a single node for parameter propagation
9897 : : AstNode* V3Width::widthParamsEdit(AstNode* nodep) {
9898 : : UINFO(4, __FUNCTION__ << ": " << nodep);
9899 : : // We should do it in bottom-up module order, but it works in any order.
9900 : : WidthVisitor visitor{true, false};
9901 : : nodep = visitor.mainAcceptEdit(nodep);
9902 : : // No WidthRemoveVisitor, as don't want to drop $signed etc inside gen blocks
9903 : : return nodep;
9904 : : }
9905 : :
9906 : : //! Single node parameter propagation for generate blocks.
9907 : : //! Smaller step... Only do a single node for parameter propagation
9908 : : //! If we are inside a generated "if", "case" or "for", we don't want to
9909 : : //! trigger warnings when we deal with the width. It is possible that
9910 : : //! these are spurious, existing within sub-expressions that will not
9911 : : //! actually be generated. Since such occurrences, must be constant, in
9912 : : //! order to be something a generate block can depend on, we can wait until
9913 : : //! later to do the width check.
9914 : : //! @return Pointer to the edited node.
9915 : : AstNode* V3Width::widthGenerateParamsEdit(
9916 : : AstNode* nodep) { //!< [in] AST whose parameters widths are to be analyzed.
9917 : : UINFO(4, __FUNCTION__ << ": " << nodep);
9918 : : // We should do it in bottom-up module order, but it works in any order.
9919 : : WidthVisitor visitor{true, true};
9920 : : nodep = visitor.mainAcceptEdit(nodep);
9921 : : // No WidthRemoveVisitor, as don't want to drop $signed etc inside gen blocks
9922 : : return nodep;
9923 : : }
|