• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

nickg / nvc / 18428170938

11 Oct 2025 09:39AM UTC coverage: 92.549% (-0.002%) from 92.551%
18428170938

push

github

nickg
Better handling of System Verilog nulls

40 of 44 new or added lines in 4 files covered. (90.91%)

2 existing lines in 2 files now uncovered.

74468 of 80463 relevant lines covered (92.55%)

446696.77 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

96.46
/src/vlog/vlog-sem.c
1
//
2
//  Copyright (C) 2022-2025  Nick Gasson
3
//
4
//  This program is free software: you can redistribute it and/or modify
5
//  it under the terms of the GNU General Public License as published by
6
//  the Free Software Foundation, either version 3 of the License, or
7
//  (at your option) any later version.
8
//
9
//  This program is distributed in the hope that it will be useful,
10
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
11
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
//  GNU General Public License for more details.
13
//
14
//  You should have received a copy of the GNU General Public License
15
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
16
//
17

18
#include "util.h"
19
#include "diag.h"
20
#include "ident.h"
21
#include "vlog/vlog-node.h"
22
#include "vlog/vlog-number.h"
23
#include "vlog/vlog-phase.h"
24
#include "vlog/vlog-util.h"
25

26
#include <assert.h>
27
#include <stdlib.h>
28

29
typedef uint32_t type_mask_t;
30
#define TM(x) (1 << (x))
31
#define TM_ERROR  0
32
#define TM_ANY    ~0
33
#define TM_EMPTY  TM(31)
34
#define TM_CONST  TM(30)
35
#define TM_CLASS  TM(29)
36
#define TM_STRUCT TM(28)
37
#define TM_ENUM   TM(27)
38
#define TM_INTEGRAL \
39
   (TM(DT_LOGIC) | TM(DT_INTEGER) | TM(DT_BYTE) | TM(DT_SHORTINT) \
40
    | TM(DT_INT) | TM(DT_LONGINT) | TM(DT_TIME) | TM(DT_BIT) | TM_ENUM \
41
    | TM(DT_IMPLICIT))
42
#define TM_REAL (TM(DT_REAL) | TM(DT_SHORTREAL))
43
#define TM_STRICT (TM_STRUCT | TM_CLASS)
44

45
static void vlog_check_decls(vlog_node_t v);
46
static void vlog_check_stmts(vlog_node_t v);
47
static void vlog_check_ranges(vlog_node_t v);
48
static type_mask_t vlog_check_expr(vlog_node_t v);
49
static type_mask_t vlog_check_const_expr(vlog_node_t v);
50

51
static void name_for_diag(diag_t *d, vlog_node_t v, const char *alt)
5✔
52
{
53
   switch (vlog_kind(v)) {
5✔
54
   case V_REF:
5✔
55
      diag_printf(d, "'%s'", istr(vlog_ident(v)));
5✔
56
      break;
5✔
57
   default:
×
58
      diag_printf(d, "%s", alt);
×
59
      break;
×
60
   }
61
}
5✔
62

63
static type_mask_t get_type_mask(vlog_node_t v)
4,539✔
64
{
65
   switch (vlog_kind(v)) {
8,664✔
66
   case V_DATA_TYPE:
4,185✔
67
      return TM(vlog_subkind(v));
4,185✔
68
   case V_STRUCT_DECL:
69
      return TM_STRUCT;
70
   case V_CLASS_DECL:
93✔
71
      return TM_CLASS;
93✔
72
   case V_ENUM_DECL:
34✔
73
      return TM_ENUM;
34✔
74
   case V_ENUM_NAME:
19✔
75
      return TM_ENUM | TM_CONST;
19✔
76
   case V_PARAM_DECL:
205✔
77
   case V_LOCALPARAM:
78
   case V_GENVAR_DECL:
79
      return get_type_mask(vlog_type(v)) | TM_CONST;
205✔
80
   case V_VAR_DECL:
4,125✔
81
   case V_NET_DECL:
82
   case V_PORT_DECL:
83
   case V_TF_PORT_DECL:
84
   case V_TYPE_DECL:
85
   case V_FUNC_DECL:
86
      return get_type_mask(vlog_type(v));
4,125✔
87
   default:
×
88
      CANNOT_HANDLE(v);
×
89
   }
90
}
91

92
static const char *type_mask_str(type_mask_t tm)
18✔
93
{
94
   switch (tm & ~(TM_CONST)) {
18✔
95
   case TM(DT_LOGIC): return "logic";
96
   case TM(DT_BIT): return "bit";
×
97
   case TM(DT_BYTE): return "byte";
×
98
   case TM(DT_INT): return "int";
1✔
99
   case TM(DT_SHORTINT): return "shortint";
×
100
   case TM(DT_REAL): return "real";
7✔
101
   case TM(DT_SHORTREAL): return "shortreal";
×
102
   case TM(DT_TIME): return "time";
×
103
   case TM_REAL: return "real";
1✔
104
   case TM_CLASS: return "class";
4✔
105
   case TM_STRUCT: return "struct";
1✔
UNCOV
106
   case TM_ENUM: return "enum";
×
107
   default: return (tm & TM_INTEGRAL) ? "integral" : "unknown";
4✔
108
   }
109
}
110

111
static void vlog_check_variable_lvalue(vlog_node_t v, vlog_node_t where)
1,380✔
112
{
113
   switch (vlog_kind(v)) {
2,850✔
114
   case V_VAR_DECL:
115
   case V_FUNC_DECL:
116
   case V_STRUCT_DECL:
117
   case V_ENUM_DECL:
118
   case V_UNION_DECL:
119
   case V_GENVAR_DECL:
120
      return;
121
   case V_REF:
1,377✔
122
      if (vlog_has_ref(v))
1,377✔
123
         vlog_check_variable_lvalue(vlog_ref(v), v);
1,377✔
124
      return;
125
   case V_BIT_SELECT:
84✔
126
   case V_PART_SELECT:
127
   case V_MEMBER_REF:
128
      vlog_check_variable_lvalue(vlog_value(v), v);
84✔
129
      return;
84✔
130
   case V_CONCAT:
3✔
131
      {
132
         const int nparams = vlog_params(v);
3✔
133
         for (int i = 0; i < nparams; i++) {
12✔
134
            vlog_node_t p = vlog_param(v, i);
9✔
135
            vlog_check_variable_lvalue(p, p);
9✔
136
         }
137
      }
138
      return;
139
   case V_TF_PORT_DECL:
1✔
140
      if (vlog_subkind(v) != V_PORT_INPUT)
1✔
141
         return;
142
      break;
143
   case V_PORT_DECL:
31✔
144
      if (vlog_has_ref(v)) {
31✔
145
         vlog_check_variable_lvalue(vlog_ref(v), where);
9✔
146
         return;
9✔
147
      }
148
      else if (!is_implicit_data_type(vlog_type(v)))
22✔
149
         return;
150
      break;
151
   default:
152
      break;
153
   }
154

155
   diag_t *d = diag_new(DIAG_ERROR, vlog_loc(where));
3✔
156
   name_for_diag(d, where, "target");
3✔
157
   diag_printf(d, " cannot be assigned in a procedural block");
3✔
158
   diag_emit(d);
3✔
159
}
160

161
static void vlog_check_net_lvalue(vlog_node_t v, vlog_node_t where)
224✔
162
{
163
   switch (vlog_kind(v)) {
478✔
164
   case V_NET_DECL:
165
      return;
166
   case V_PORT_DECL:
53✔
167
      if (vlog_has_ref(v)) {
53✔
168
         vlog_check_net_lvalue(vlog_ref(v), where);
9✔
169
         return;
9✔
170
      }
171
      else if (is_implicit_data_type(vlog_type(v)))
44✔
172
         return;
173
      break;
174
   case V_REF:
219✔
175
      vlog_check_net_lvalue(vlog_ref(v), v);
219✔
176
      return;
219✔
177
   case V_BIT_SELECT:
26✔
178
   case V_PART_SELECT:
179
      vlog_check_net_lvalue(vlog_value(v), v);
26✔
180
      return;
26✔
181
   case V_CONCAT:
5✔
182
      {
183
         const int nparams = vlog_params(v);
5✔
184
         for (int i = 0; i < nparams; i++) {
15✔
185
            vlog_node_t p = vlog_param(v, i);
10✔
186
            vlog_check_net_lvalue(p, p);
10✔
187
         }
188
      }
189
      return;
190
   default:
191
      break;
192
   }
193

194
   diag_t *d = diag_new(DIAG_ERROR, vlog_loc(where));
2✔
195
   name_for_diag(d, where, "target");
2✔
196
   diag_printf(d, " cannot be driven by continuous assignment");
2✔
197
   diag_emit(d);
2✔
198
}
199

200
static void vlog_check_same_type(vlog_node_t left, type_mask_t lmask,
2,730✔
201
                                 vlog_node_t right, type_mask_t rmask)
202
{
203
   if (lmask == TM_ERROR || rmask == TM_ERROR)
2,730✔
204
      return;   // Suppress cascading errors
205
   else if (((lmask | rmask) & TM_STRICT) == 0)
2,723✔
206
      return;   // Convertible
207
   else if (lmask & rmask) {
25✔
208
      if (vlog_kind(left) == V_NULL)
23✔
NEW
209
         vlog_set_type(left, vlog_get_type(right));
×
210
      else if (vlog_kind(right) == V_NULL)
23✔
211
         vlog_set_type(right, vlog_get_type(left));
13✔
212
      else {
213
         vlog_node_t ltype = vlog_get_type(left);
10✔
214
         vlog_node_t rtype = vlog_get_type(right);
10✔
215

216
         if (ltype != rtype)
10✔
217
            error_at(vlog_loc(right), "value of type '%s' is not compatible "
2✔
218
                     "with '%s'", istr(vlog_ident(rtype)),
219
                     istr(vlog_ident(ltype)));
220
      }
221
   }
222
   else
223
      error_at(vlog_loc(right), "value of type '%s' is not compatible with "
2✔
224
               "'%s'", type_mask_str(rmask), type_mask_str(lmask));
225
}
226

227
static void vlog_check_decls(vlog_node_t v)
1,140✔
228
{
229
   const int ndecls = vlog_decls(v);
1,140✔
230
   for (int i = 0; i < ndecls; i++)
2,458✔
231
      vlog_check(vlog_decl(v, i));
1,318✔
232
}
1,140✔
233

234
static void vlog_check_stmts(vlog_node_t v)
3,070✔
235
{
236
   const int nstmts = vlog_stmts(v);
3,070✔
237
   for (int i = 0; i < nstmts; i++)
8,350✔
238
      vlog_check(vlog_stmt(v, i));
5,280✔
239
}
3,070✔
240

241
static void vlog_check_ranges(vlog_node_t v)
1,791✔
242
{
243
   const int nranges = vlog_ranges(v);
1,791✔
244
   for (int i = 0; i < nranges; i++)
2,208✔
245
      vlog_check(vlog_range(v, i));
417✔
246
}
1,791✔
247

248
static void vlog_check_params(vlog_node_t v)
1,384✔
249
{
250
   const int nparams = vlog_params(v);
1,384✔
251
   for (int i = 0; i < nparams; i++)
3,249✔
252
      vlog_check_expr(vlog_param(v, i));
1,865✔
253
}
1,384✔
254

255
static void vlog_check_nbassign(vlog_node_t v)
124✔
256
{
257
   vlog_node_t target = vlog_target(v);
124✔
258
   type_mask_t lmask = vlog_check_expr(target);
124✔
259

260
   vlog_check_variable_lvalue(target, target);
124✔
261

262
   vlog_node_t value = vlog_value(v);
124✔
263
   type_mask_t rmask = vlog_check_expr(value);
124✔
264

265
   vlog_check_same_type(target, lmask, value, rmask);
124✔
266
}
124✔
267

268
static void vlog_check_bassign(vlog_node_t v)
1,157✔
269
{
270
   vlog_node_t target = vlog_target(v);
1,157✔
271
   type_mask_t lmask = vlog_check_expr(target);
1,157✔
272

273
   vlog_check_variable_lvalue(target, target);
1,157✔
274

275
   vlog_node_t value = vlog_value(v);
1,157✔
276
   type_mask_t rmask = vlog_check_expr(value);
1,157✔
277

278
   vlog_check_same_type(target, lmask, value, rmask);
1,157✔
279
}
1,157✔
280

281
static void vlog_check_assign(vlog_node_t v)
214✔
282
{
283
   vlog_node_t target = vlog_target(v);
214✔
284
   type_mask_t lmask = vlog_check_expr(target);
214✔
285

286
   vlog_check_net_lvalue(target, target);
214✔
287

288
   vlog_node_t value = vlog_value(v);
214✔
289
   type_mask_t rmask = vlog_check_expr(value);
214✔
290

291
   vlog_check_same_type(target, lmask, value, rmask);
214✔
292
}
214✔
293

294
static void vlog_check_consistent(vlog_node_t a, vlog_node_t b)
37✔
295
{
296
   vlog_node_t at = vlog_type(a);
37✔
297
   vlog_node_t bt = vlog_type(b);
37✔
298

299
   if (at == bt)
37✔
300
      return;
301

302
   const int aranges = vlog_ranges(at);
37✔
303
   assert(aranges == vlog_ranges(bt));
37✔
304

305
   for (int i = 0; i < aranges; i++) {
45✔
306
      vlog_node_t ar = vlog_range(at, i);
8✔
307
      vlog_node_t br = vlog_range(bt, i);
8✔
308

309
      vlog_node_t aleft = vlog_left(ar);
8✔
310
      vlog_node_t bleft = vlog_left(br);
8✔
311

312
      vlog_node_t aright = vlog_right(ar);
8✔
313
      vlog_node_t bright = vlog_right(br);
8✔
314

315
      if (!vlog_equal_node(aleft, bleft) || !vlog_equal_node(aright, bright)) {
8✔
316
         diag_t *d = diag_new(DIAG_ERROR, vlog_loc(br));
1✔
317
         diag_printf(d, "inconsistent dimensions for '%s'",
1✔
318
                     istr(vlog_ident(b)));
319
         diag_hint(d, vlog_loc(a), "earlier declaration here");
1✔
320
         diag_emit(d);
1✔
321
      }
322
   }
323
}
324

325
static void vlog_check_port_decl(vlog_node_t v)
267✔
326
{
327
   if (vlog_has_ref(v)) {
267✔
328
      vlog_node_t ref = vlog_ref(v);
37✔
329
      vlog_check_consistent(v, ref);
37✔
330

331
      if (!is_implicit_data_type(vlog_type(v))) {
37✔
332
         diag_t *d = diag_new(DIAG_ERROR, vlog_loc(ref));
1✔
333
         diag_printf(d, "completely declared port '%s' cannot be declared "
1✔
334
                     "again in a data type declaration", istr(vlog_ident(v)));
335
         diag_hint(d, vlog_loc(v), "port declaration contains a data type");
1✔
336
         diag_hint(d, vlog_loc(ref), "redeclared here");
1✔
337
         diag_emit(d);
1✔
338
      }
339
   }
340

341
   if (vlog_has_value(v))
267✔
342
      vlog_check_expr(vlog_value(v));
×
343
}
267✔
344

345
static void vlog_check_net_decl(vlog_node_t v)
296✔
346
{
347
   vlog_check(vlog_type(v));
296✔
348
   vlog_check_ranges(v);
296✔
349

350
   if (vlog_has_value(v))
296✔
351
      vlog_check_expr(vlog_value(v));
27✔
352
}
296✔
353

354
static void vlog_check_var_decl(vlog_node_t v)
612✔
355
{
356
   vlog_check(vlog_type(v));
612✔
357
   vlog_check_ranges(v);
612✔
358

359
   if (vlog_has_value(v))
612✔
360
      vlog_check_expr(vlog_value(v));
54✔
361
}
612✔
362

363
static void vlog_check_param_decl(vlog_node_t v)
62✔
364
{
365
   if (vlog_has_value(v))
62✔
366
      vlog_check_expr(vlog_value(v));
50✔
367
}
62✔
368

369
static void vlog_check_genvar_decl(vlog_node_t v)
370
{
371
}
372

373
static void vlog_check_type_decl(vlog_node_t v)
10✔
374
{
375
   vlog_check(vlog_type(v));
10✔
376
}
10✔
377

378
static void vlog_check_data_type(vlog_node_t v)
883✔
379
{
380
   vlog_check_ranges(v);
883✔
381
}
883✔
382

383
static void vlog_check_enum_decl(vlog_node_t v)
16✔
384
{
385
   vlog_check_decls(v);
16✔
386
}
16✔
387

388
static void vlog_check_struct_decl(vlog_node_t v)
2✔
389
{
390
   vlog_check_decls(v);
2✔
391
}
2✔
392

393
static void vlog_check_union_decl(vlog_node_t v)
2✔
394
{
395
   vlog_check_decls(v);
2✔
396
}
2✔
397

398
static void vlog_check_tf_decl(vlog_node_t v)
28✔
399
{
400
   const int nports = vlog_ports(v);
28✔
401
   for (int i = 0; i < nports; i++)
66✔
402
      vlog_check(vlog_port(v, i));
38✔
403

404
   vlog_check_decls(v);
28✔
405
   vlog_check_stmts(v);
28✔
406
}
28✔
407

408
static void vlog_check_primitive(vlog_node_t udp)
18✔
409
{
410
   const int nports = vlog_ports(udp);
18✔
411
   for (int i = 0; i < nports; i++) {
75✔
412
      vlog_node_t p = vlog_port(udp, i);
57✔
413

414
      if (vlog_has_ref(p)) {
57✔
415
         vlog_node_t decl = vlog_ref(p);
56✔
416
         assert(vlog_kind(decl) == V_PORT_DECL);
56✔
417

418
         if (i == 0 && vlog_subkind(decl) != V_PORT_OUTPUT) {
57✔
419
            diag_t *d = diag_new(DIAG_ERROR, vlog_loc(p));
1✔
420
            diag_printf(d, "the first port of a primitive must be an output");
1✔
421
            diag_hint(d, vlog_loc(decl), "port declaration here");
1✔
422
            diag_emit(d);
1✔
423
         }
424
         else if (i > 0 && vlog_subkind(decl) != V_PORT_INPUT) {
55✔
425
            diag_t *d = diag_new(DIAG_ERROR, vlog_loc(p));
1✔
426
            diag_printf(d, "all ports of a primitive except the first must "
1✔
427
                        "be inputs");
428
            diag_hint(d, vlog_loc(decl), "port declaration here");
1✔
429
            diag_emit(d);
1✔
430
         }
431
      }
432
   }
433

434
   assert(vlog_stmts(udp) == 1);
18✔
435

436
   vlog_node_t table = vlog_stmt(udp, 0);
18✔
437
   assert(vlog_kind(table) == V_UDP_TABLE);
18✔
438

439
   const vlog_udp_kind_t kind = vlog_subkind(table);
18✔
440
   const int expect = kind == V_UDP_SEQ ? nports + 1 : nports;
18✔
441

442
   const int nparams = vlog_params(table);
18✔
443
   for (int i = 0; i < nparams; i++) {
114✔
444
      vlog_node_t row = vlog_param(table, i);
97✔
445
      assert(vlog_kind(row) == V_UDP_ENTRY);
97✔
446

447
      const int nsymbols = vlog_params(row);
97✔
448
      if (nsymbols != expect) {
97✔
449
         error_at(vlog_loc(row), "expected %d symbols in UDP table entry but "
1✔
450
                  "have %d", expect, nsymbols);
451
         break;
1✔
452
      }
453
   }
454
}
18✔
455

456
static void vlog_check_dimension(vlog_node_t v)
417✔
457
{
458
   vlog_node_t left = vlog_left(v);
417✔
459
   vlog_check_const_expr(left);
417✔
460

461
   vlog_node_t right = vlog_right(v);
417✔
462
   vlog_check_const_expr(right);
417✔
463
}
417✔
464

465
static void vlog_check_localparam(vlog_node_t v)
19✔
466
{
467
   if (vlog_has_value(v))
19✔
468
      vlog_check_expr(vlog_value(v));
18✔
469
   else
470
      error_at(vlog_loc(v), "local parameter declaration must have a "
1✔
471
               "default value");
472
}
19✔
473

474
static void vlog_check_case(vlog_node_t v)
8✔
475
{
476
   vlog_check_expr(vlog_value(v));
8✔
477

478
   bool seen_default = false;
8✔
479
   const int nstmts = vlog_stmts(v);
8✔
480
   for (int i = 0; i < nstmts; i++) {
48✔
481
      vlog_node_t item = vlog_stmt(v, i);
40✔
482
      assert(vlog_kind(item) == V_CASE_ITEM);
40✔
483

484
      const int nparams = vlog_params(item);
40✔
485
      if (nparams == 0 && !seen_default)
40✔
486
         seen_default = true;
487
      else if (nparams == 0)
35✔
488
         error_at(vlog_loc(item), "multiple default statements within a single "
1✔
489
                  "case statement");
490

491
      vlog_check_stmts(item);
40✔
492
   }
493
}
8✔
494

495
static void vlog_check_for_loop(vlog_node_t v)
48✔
496
{
497
   vlog_node_t init = vlog_left(v);
48✔
498
   assert(vlog_kind(init) == V_FOR_INIT);
48✔
499
   vlog_check_decls(init);
48✔
500
   vlog_check_stmts(init);
48✔
501

502
   vlog_check_expr(vlog_value(v));
48✔
503

504
   vlog_node_t step = vlog_right(v);
48✔
505
   assert(vlog_kind(step) == V_FOR_STEP);
48✔
506
   vlog_check_stmts(step);
48✔
507

508
   vlog_check_stmts(v);
48✔
509
}
48✔
510

511
static void vlog_check_repeat(vlog_node_t v)
3✔
512
{
513
   vlog_check_expr(vlog_value(v));
3✔
514

515
   vlog_check_stmts(v);
3✔
516
}
3✔
517

518
static void vlog_check_while(vlog_node_t v)
3✔
519
{
520
   vlog_check_expr(vlog_value(v));
3✔
521

522
   vlog_check_stmts(v);
3✔
523
}
3✔
524

525
static void vlog_check_call_args(vlog_node_t v, vlog_node_t sub)
53✔
526
{
527
   const int nparams = vlog_params(v);
53✔
528
   const int nports = vlog_ports(sub);
53✔
529

530
   if (nparams != nports) {
53✔
531
      diag_t *d = diag_new(DIAG_ERROR, vlog_loc(v));
2✔
532
      diag_printf(d, "expected %d argument%s for '%s' but have %d", nports,
2✔
533
                  nports != 1 ? "s" : "", istr(vlog_ident(sub)), nparams);
534
      diag_hint(d, vlog_loc(sub), "'%s' declared here", istr(vlog_ident(sub)));
2✔
535
      diag_emit(d);
2✔
536
      return;
2✔
537
   }
538

539
   for (int i = 0; i < nparams; i++)
140✔
540
      vlog_check_expr(vlog_param(v, i));
89✔
541
}
542

543
static void vlog_check_user_tcall(vlog_node_t v)
3✔
544
{
545
   vlog_node_t func = vlog_ref(v);
3✔
546
   if (vlog_kind(func) != V_TASK_DECL) {
3✔
547
      diag_t *d = diag_new(DIAG_ERROR, vlog_loc(v));
1✔
548
      diag_printf(d, "'%s' is not a task", istr(vlog_ident(func)));
1✔
549
      diag_hint(d, vlog_loc(func), "'%s' declared here",
1✔
550
                istr(vlog_ident(func)));
551
      diag_emit(d);
1✔
552
      return;
1✔
553
   }
554

555
   vlog_check_call_args(v, func);
2✔
556
}
557

558
static void vlog_check_super_call(vlog_node_t v)
559
{
560
   // TODO: vlog_check_call_args()
561
}
562

563
static void vlog_check_deassign(vlog_node_t v)
×
564
{
565
   error_at(vlog_loc(v), "procedural deassign statements are not supported "
×
566
            "as they are being considered for removal from the System Verilog "
567
            "standard");
568
}
×
569

570
static void vlog_check_return(vlog_node_t v)
5✔
571
{
572
   if (!vlog_has_ref(v))
5✔
573
      return;   // Was earlier error
574

575
   vlog_node_t subr = vlog_ref(v);
5✔
576
   const vlog_kind_t kind = vlog_kind(subr);
5✔
577

578
   if (kind == V_FUNC_DECL && !vlog_has_value(v))
5✔
579
      error_at(vlog_loc(v), "return statement in a non-void function must "
1✔
580
               "have an expression");
581
   else if (kind == V_TASK_DECL && vlog_has_value(v))
4✔
582
      error_at(vlog_loc(v), "return statement in a task cannot have an "
1✔
583
               "expression");
584
}
585

586
static void vlog_check_sys_tcall(vlog_node_t v)
1,127✔
587
{
588
   vlog_check_params(v);
1,127✔
589
}
1,127✔
590

591
static void vlog_check_timing(vlog_node_t v)
694✔
592
{
593
   vlog_check(vlog_value(v));
694✔
594
   vlog_check_stmts(v);
694✔
595
}
694✔
596

597
static void vlog_check_if(vlog_node_t v)
531✔
598
{
599
   const int nconds = vlog_conds(v);
531✔
600
   for (int i = 0; i < nconds; i++)
1,176✔
601
      vlog_check(vlog_cond(v, i));
645✔
602
}
531✔
603

604
static void vlog_check_if_generate(vlog_node_t v)
25✔
605
{
606
   const int nconds = vlog_conds(v);
25✔
607
   for (int i = 0; i < nconds; i++) {
60✔
608
      vlog_node_t c = vlog_cond(v, i);
35✔
609
      assert(vlog_kind(c) == V_COND);
35✔
610

611
      if (vlog_has_value(c)) {
35✔
612
         vlog_node_t value = vlog_value(c);
25✔
613
         vlog_check_const_expr(value);
25✔
614
      }
615

616
      vlog_check_stmts(c);
35✔
617
   }
618
}
25✔
619

620
static void vlog_check_cond(vlog_node_t v)
645✔
621
{
622
   if (vlog_has_value(v))
645✔
623
      vlog_check_expr(vlog_value(v));
540✔
624

625
   vlog_check_stmts(v);
645✔
626
}
645✔
627

628
static void vlog_check_event_control(vlog_node_t v)
147✔
629
{
630
   const int nparams = vlog_params(v);
147✔
631
   for (int i = 0; i < nparams; i++)
276✔
632
      vlog_check_expr(vlog_param(v, i));
129✔
633
}
147✔
634

635
static void vlog_check_delay_control(vlog_node_t v)
547✔
636
{
637
   vlog_check_expr(vlog_value(v));
547✔
638
}
547✔
639

640
static void vlog_check_module(vlog_node_t v)
311✔
641
{
642
   const int nports = vlog_ports(v);
311✔
643
   for (int i = 0; i < nports; i++) {
541✔
644
      vlog_node_t ref = vlog_port(v, i);
230✔
645
      if (!vlog_has_ref(ref))
230✔
646
         error_at(vlog_loc(ref), "missing port declaration for '%s'",
1✔
647
                  istr(vlog_ident(ref)));
648
   }
649

650
   vlog_check_decls(v);
311✔
651
   vlog_check_stmts(v);
311✔
652
}
311✔
653

654
static void vlog_check_gate_inst(vlog_node_t v)
36✔
655
{
656
   const int nparams = vlog_params(v);
36✔
657
   for (int i = 0; i < nparams; i++)
88✔
658
      vlog_check_expr(vlog_param(v, i));
52✔
659
}
36✔
660

661
static void vlog_check_inst_list(vlog_node_t v)
52✔
662
{
663
   const int nparams = vlog_params(v);
52✔
664
   for (int i = 0; i < nparams; i++)
82✔
665
      vlog_check(vlog_param(v, i));
30✔
666

667
   vlog_check_stmts(v);
52✔
668
}
52✔
669

670
static void vlog_check_mod_inst(vlog_node_t v)
52✔
671
{
672
   const int nparams = vlog_params(v);
52✔
673
   for (int i = 0; i < nparams; i++)
145✔
674
      vlog_check(vlog_param(v, i));
93✔
675
}
52✔
676

677
static void vlog_check_port_conn(vlog_node_t v)
93✔
678
{
679
   if (vlog_has_value(v))
93✔
680
      vlog_check_expr(vlog_value(v));
93✔
681
}
93✔
682

683
static void vlog_check_param_assign(vlog_node_t v)
30✔
684
{
685
   if (vlog_has_value(v))
30✔
686
      vlog_check_const_expr(vlog_value(v));
30✔
687
}
30✔
688

689
static void vlog_check_enum_name(vlog_node_t v)
44✔
690
{
691
   if (vlog_has_value(v))
44✔
692
      vlog_check_const_expr(vlog_value(v));
11✔
693
}
44✔
694

695
static void vlog_check_wait(vlog_node_t v)
696
{
697
   // TODO
698
}
699

700
static type_mask_t vlog_check_hier_ref(vlog_node_t v)
3✔
701
{
702
   vlog_node_t inst = vlog_ref(v);
3✔
703
   if (vlog_kind(inst) != V_MOD_INST)
3✔
704
      error_at(vlog_loc(v), "prefix of hierarchical identifier is not an "
1✔
705
               "instance");
706

707
   return TM_INTEGRAL;
3✔
708
}
709

710
static type_mask_t vlog_check_member_ref(vlog_node_t v)
63✔
711
{
712
   vlog_check_expr(vlog_value(v));
63✔
713

714
   return get_type_mask(vlog_ref(v));
63✔
715
}
716

717
static type_mask_t vlog_check_ref(vlog_node_t v)
4,220✔
718
{
719
   return get_type_mask(vlog_ref(v));
4,220✔
720
}
721

722
static type_mask_t vlog_check_index(vlog_node_t v)
238✔
723
{
724
   type_mask_t tmask = vlog_check_expr(vlog_value(v));
238✔
725
   if (tmask == TM_ERROR)
238✔
726
      return TM_ERROR;
727
   else if (!(tmask & TM_INTEGRAL)) {
238✔
728
      error_at(vlog_loc(v), "value of type '%s' cannot be indexed",
1✔
729
               type_mask_str(tmask));
730
      return TM_ERROR;
1✔
731
   }
732

733
   return tmask;
734
}
735

736
static type_mask_t vlog_check_bit_select(vlog_node_t v)
163✔
737
{
738
   vlog_check_params(v);
163✔
739

740
   return vlog_check_index(v);
163✔
741
}
742

743
static type_mask_t vlog_check_part_select(vlog_node_t v)
75✔
744
{
745
   if (vlog_subkind(v) == V_RANGE_CONST) {
75✔
746
      vlog_node_t left = vlog_left(v);
57✔
747
      vlog_check_const_expr(left);
57✔
748
   }
749

750
   vlog_node_t right = vlog_right(v);
75✔
751
   vlog_check_const_expr(right);
75✔
752

753
   return vlog_check_index(v);
75✔
754
}
755

756
static type_mask_t vlog_check_user_fcall(vlog_node_t v)
52✔
757
{
758
   vlog_node_t func = vlog_ref(v);
52✔
759
   if (vlog_kind(func) != V_FUNC_DECL) {
52✔
760
      diag_t *d = diag_new(DIAG_ERROR, vlog_loc(v));
1✔
761
      diag_printf(d, "'%s' is not a function", istr(vlog_ident(func)));
1✔
762
      diag_hint(d, vlog_loc(func), "'%s' declared here",
1✔
763
                istr(vlog_ident(func)));
764
      diag_emit(d);
1✔
765
      return TM_ERROR;
1✔
766
   }
767

768
   vlog_check_call_args(v, func);
51✔
769

770
   return get_type_mask(func);
51✔
771
}
772

773
static type_mask_t vlog_check_concat(vlog_node_t v)
46✔
774
{
775
   if (vlog_has_value(v)) {
46✔
776
      vlog_node_t repeat = vlog_value(v);
14✔
777
      vlog_check_const_expr(repeat);
14✔
778
   }
779

780
   const int nparams = vlog_params(v);
46✔
781
   for (int i = 0; i < nparams; i++)
133✔
782
      vlog_check_expr(vlog_param(v, i));
87✔
783

784
   return TM(DT_LOGIC);
46✔
785
}
786

787
static type_mask_t vlog_check_event(vlog_node_t v)
129✔
788
{
789
   vlog_check_expr(vlog_value(v));
129✔
790
   return TM(DT_LOGIC);
129✔
791
}
792

793
static type_mask_t vlog_check_binary(vlog_node_t v)
1,241✔
794
{
795
   vlog_node_t left = vlog_left(v);
1,241✔
796
   vlog_node_t right = vlog_right(v);
1,241✔
797

798
   type_mask_t lmask = vlog_check_expr(left);
1,241✔
799
   type_mask_t rmask = vlog_check_expr(right);
1,241✔
800

801
   if ((lmask & TM_REAL) && (rmask & TM_INTEGRAL))
1,241✔
802
      rmask |= TM_REAL;
2✔
803

804
   if ((rmask & TM_REAL) && (lmask & TM_INTEGRAL))
1,241✔
805
      lmask |= TM_REAL;
1✔
806

807
   if ((lmask & TM_INTEGRAL) && (rmask & TM_INTEGRAL)) {
1,241✔
808
      lmask |= TM_INTEGRAL;
1,224✔
809
      rmask |= TM_INTEGRAL;
1,224✔
810
   }
811

812
   // See table 11-1 in 1800-2023 section 11.3 for allowed operand types
813
   type_mask_t allow = TM_ANY, result = lmask & rmask;
1,241✔
814
   switch (vlog_subkind(v)) {
1,241✔
815
   case V_BINARY_PLUS:
393✔
816
   case V_BINARY_MINUS:
817
   case V_BINARY_TIMES:
818
   case V_BINARY_DIVIDE:
819
   case V_BINARY_EXP:
820
   case V_BINARY_LOG_OR:
821
   case V_BINARY_LOG_AND:
822
      allow = TM_INTEGRAL | TM_REAL | TM_CONST;
393✔
823
      break;
393✔
824
   case V_BINARY_MOD:
117✔
825
   case V_BINARY_OR:
826
   case V_BINARY_AND:
827
   case V_BINARY_XOR:
828
   case V_BINARY_XNOR:
829
   case V_BINARY_SHIFT_LL:
830
   case V_BINARY_SHIFT_RL:
831
   case V_BINARY_SHIFT_LA:
832
   case V_BINARY_SHIFT_RA:
833
      allow = TM_INTEGRAL | TM_CONST;
117✔
834
      break;
117✔
835
   case V_BINARY_CASE_EQ:
517✔
836
   case V_BINARY_CASE_NEQ:
837
      allow = TM_ANY & ~TM_REAL;
517✔
838
      result = TM(DT_BIT) | (lmask & rmask & TM_CONST);
517✔
839
      break;
517✔
840
   }
841

842
   if (lmask & rmask & allow) {
1,241✔
843
      vlog_check_same_type(left, lmask, right, rmask);
1,235✔
844
      return result;
1,235✔
845
   }
846

847
   diag_t *d = diag_new(DIAG_ERROR, vlog_loc(v));
6✔
848
   diag_printf(d, "invalid operands for binary expression");
6✔
849
   diag_hint(d, vlog_loc(v), "have '%s' and '%s'", type_mask_str(lmask),
6✔
850
             type_mask_str(rmask));
851
   diag_emit(d);
6✔
852

853
   return TM_ERROR;
6✔
854
}
855

856
static type_mask_t vlog_check_unary(vlog_node_t v)
152✔
857
{
858
   type_mask_t tmask = vlog_check_expr(vlog_value(v));
152✔
859

860
   // See table 11-1 in 1800-2023 section 11.3 for allowed operand types
861
   type_mask_t allow = TM_ANY, result = tmask;
152✔
862
   switch (vlog_subkind(v)) {
152✔
863
   case V_UNARY_BITNEG:
67✔
864
   case V_UNARY_AND:
865
   case V_UNARY_OR:
866
   case V_UNARY_NAND:
867
   case V_UNARY_NOR:
868
   case V_UNARY_XOR:
869
   case V_UNARY_XNOR:
870
      allow = TM_INTEGRAL | TM_CONST;
67✔
871
      break;
67✔
872
   case V_UNARY_IDENTITY:
85✔
873
   case V_UNARY_NEG:
874
   case V_UNARY_NOT:
875
      allow = TM_INTEGRAL | TM_REAL | TM_CONST;
85✔
876
      break;
85✔
877
   }
878

879
   if (tmask == TM_ERROR)
152✔
880
      return TM_ERROR;
881
   else if (tmask & allow)
152✔
882
      return result;
883

884
   diag_t *d = diag_new(DIAG_ERROR, vlog_loc(v));
1✔
885
   diag_printf(d, "invalid operands for unary expression");
1✔
886
   diag_hint(d, vlog_loc(v), "have '%s'", type_mask_str(tmask));
1✔
887
   diag_emit(d);
1✔
888

889
   return TM_ERROR;
1✔
890
}
891

892
static type_mask_t vlog_check_cond_expr(vlog_node_t v)
32✔
893
{
894
   type_mask_t vmask = vlog_check_expr(vlog_value(v));
32✔
895

896
   type_mask_t lmask = vlog_check_expr(vlog_left(v));
32✔
897
   type_mask_t rmask = vlog_check_expr(vlog_right(v));
32✔
898

899
   return TM_INTEGRAL | (vmask & lmask & rmask & TM_CONST);
32✔
900
}
901

902
static type_mask_t vlog_check_sys_fcall(vlog_node_t v)
94✔
903
{
904
   vlog_check_params(v);
94✔
905

906
   // See 1800-2023 section 11.2.1 for list of constant system functions
907
   switch (is_well_known(vlog_ident(v))) {
94✔
908
   case W_DLR_CLOG2:
909
      return TM_INTEGRAL | TM_CONST;
910
   default:
90✔
911
      return TM_INTEGRAL;
90✔
912
   }
913
}
914

915
static type_mask_t vlog_check_class_new(vlog_node_t v)
5✔
916
{
917
   const int nparams = vlog_params(v);
5✔
918
   for (int i = 0; i < nparams; i++)
5✔
919
      vlog_check_expr(vlog_param(v, i));
×
920

921
   return TM_CLASS;
5✔
922
}
923

924
static type_mask_t vlog_check_op_assign(vlog_node_t v)
75✔
925
{
926
   vlog_node_t target = vlog_target(v);
75✔
927
   type_mask_t tmask = vlog_check_expr(target);
75✔
928

929
   vlog_check_variable_lvalue(target, target);
75✔
930

931
   vlog_check_expr(vlog_value(v));
75✔
932

933
   return tmask;
75✔
934
}
935

936
static type_mask_t vlog_check_prefix_postfix(vlog_node_t v)
15✔
937
{
938
   vlog_node_t target = vlog_target(v);
15✔
939
   type_mask_t tmask = vlog_check_expr(target);
15✔
940

941
   vlog_check_variable_lvalue(target, target);
15✔
942

943
   return tmask;
15✔
944
}
945

946
static void vlog_non_const_diag_cb(vlog_node_t v, void *ctx)
4✔
947
{
948
   vlog_node_t *pdecl = ctx;
4✔
949

950
   vlog_node_t decl = vlog_ref(v);
4✔
951
   switch (vlog_kind(decl)) {
4✔
952
   case V_PARAM_DECL:
953
   case V_LOCALPARAM:
954
   case V_GENVAR_DECL:
955
   case V_ENUM_NAME:
956
      break;
957
   default:
4✔
958
      *pdecl = decl;
4✔
959
      break;
4✔
960
   }
961
}
4✔
962

963
static type_mask_t vlog_check_const_expr(vlog_node_t v)
1,046✔
964
{
965
   type_mask_t tmask = vlog_check_expr(v);
1,046✔
966

967
   if (!(tmask & TM_CONST)) {
1,046✔
968
      vlog_node_t decl = NULL;
4✔
969
      vlog_visit_only(v, vlog_non_const_diag_cb, &decl, V_REF);
4✔
970

971
      if (decl == NULL)
4✔
972
         error_at(vlog_loc(v), "expression is not a constant");
×
973
      else {
974
         diag_t *d = diag_new(DIAG_ERROR, vlog_loc(v));
4✔
975
         diag_printf(d, "cannot reference %s '%s' in constant expression",
9✔
976
                     vlog_is_net(decl) ? "net" : "variable",
4✔
977
                     istr(vlog_ident(decl)));
978
         diag_hint(d, vlog_loc(decl), "%s declared here",
4✔
979
                   istr(vlog_ident(decl)));
980
         diag_emit(d);
4✔
981
      }
982
   }
983

984
   return tmask;
1,046✔
985
}
986

987
static type_mask_t vlog_check_expr(vlog_node_t v)
10,975✔
988
{
989
   switch (vlog_kind(v)) {
10,975✔
990
   case V_BINARY:
1,241✔
991
      return vlog_check_binary(v);
1,241✔
992
   case V_UNARY:
152✔
993
      return vlog_check_unary(v);
152✔
994
   case V_COND_EXPR:
32✔
995
      return vlog_check_cond_expr(v);
32✔
996
   case V_SYS_FCALL:
94✔
997
      return vlog_check_sys_fcall(v);
94✔
998
   case V_USER_FCALL:
52✔
999
      return vlog_check_user_fcall(v);
52✔
1000
   case V_REF:
4,220✔
1001
      return vlog_check_ref(v);
4,220✔
1002
   case V_HIER_REF:
3✔
1003
      return vlog_check_hier_ref(v);
3✔
1004
   case V_MEMBER_REF:
63✔
1005
      return vlog_check_member_ref(v);
63✔
1006
   case V_BIT_SELECT:
163✔
1007
      return vlog_check_bit_select(v);
163✔
1008
   case V_PART_SELECT:
75✔
1009
      return vlog_check_part_select(v);
75✔
1010
   case V_EVENT:
129✔
1011
      return vlog_check_event(v);
129✔
1012
   case V_CLASS_NEW:
5✔
1013
      return vlog_check_class_new(v);
5✔
1014
   case V_CONCAT:
46✔
1015
      return vlog_check_concat(v);
46✔
1016
   case V_PREFIX:
3✔
1017
   case V_POSTFIX:
1018
      return vlog_check_prefix_postfix(v);
3✔
1019
   case V_NUMBER:
1020
   case V_STRENGTH:
1021
      return TM_INTEGRAL | TM_CONST;
1022
   case V_REAL:
7✔
1023
      return TM_REAL | TM_CONST;
7✔
1024
   case V_STRING:
872✔
1025
      return TM(DT_LOGIC) | TM_CONST;
872✔
1026
   case V_EMPTY:
9✔
1027
      return TM_EMPTY;
9✔
1028
   case V_NULL:
14✔
1029
      return TM_CLASS | TM_CONST;
14✔
1030
   default:
×
1031
      CANNOT_HANDLE(v);
×
1032
   }
1033
}
1034

1035
void vlog_check(vlog_node_t v)
9,767✔
1036
{
1037
   switch (vlog_kind(v)) {
9,767✔
1038
   case V_MODULE:
311✔
1039
      vlog_check_module(v);
311✔
1040
      break;
311✔
1041
   case V_PACKAGE:
20✔
1042
   case V_CLASS_DECL:
1043
      vlog_check_decls(v);
20✔
1044
      break;
20✔
1045
   case V_NET_DECL:
296✔
1046
      vlog_check_net_decl(v);
296✔
1047
      break;
296✔
1048
   case V_VAR_DECL:
612✔
1049
      vlog_check_var_decl(v);
612✔
1050
      break;
612✔
1051
   case V_PORT_DECL:
267✔
1052
   case V_TF_PORT_DECL:
1053
      vlog_check_port_decl(v);
267✔
1054
      break;
267✔
1055
   case V_PARAM_DECL:
62✔
1056
      vlog_check_param_decl(v);
62✔
1057
      break;
62✔
1058
   case V_LOCALPARAM:
19✔
1059
      vlog_check_localparam(v);
19✔
1060
      break;
19✔
1061
   case V_GENVAR_DECL:
1062
      vlog_check_genvar_decl(v);
1063
      break;
1064
   case V_TYPE_DECL:
10✔
1065
      vlog_check_type_decl(v);
10✔
1066
      break;
10✔
1067
   case V_ENUM_DECL:
16✔
1068
      vlog_check_enum_decl(v);
16✔
1069
      break;
16✔
1070
   case V_STRUCT_DECL:
2✔
1071
      vlog_check_struct_decl(v);
2✔
1072
      break;
2✔
1073
   case V_UNION_DECL:
2✔
1074
      vlog_check_union_decl(v);
2✔
1075
      break;
2✔
1076
   case V_ENUM_NAME:
44✔
1077
      vlog_check_enum_name(v);
44✔
1078
      break;
44✔
1079
   case V_INITIAL:
402✔
1080
   case V_ALWAYS:
1081
   case V_FOREVER:
1082
      vlog_check_stmts(v);
402✔
1083
      break;
402✔
1084
   case V_BLOCK:
713✔
1085
   case V_PROGRAM:
1086
   case V_CONSTRUCTOR:
1087
      vlog_check_decls(v);
713✔
1088
      vlog_check_stmts(v);
713✔
1089
      break;
713✔
1090
   case V_INST_LIST:
52✔
1091
      vlog_check_inst_list(v);
52✔
1092
      break;
52✔
1093
   case V_MOD_INST:
52✔
1094
      vlog_check_mod_inst(v);
52✔
1095
      break;
52✔
1096
   case V_PORT_CONN:
93✔
1097
      vlog_check_port_conn(v);
93✔
1098
      break;
93✔
1099
   case V_PARAM_ASSIGN:
30✔
1100
      vlog_check_param_assign(v);
30✔
1101
      break;
30✔
1102
   case V_FUNC_DECL:
28✔
1103
   case V_TASK_DECL:
1104
      vlog_check_tf_decl(v);
28✔
1105
      break;
28✔
1106
   case V_TIMING:
694✔
1107
      vlog_check_timing(v);
694✔
1108
      break;
694✔
1109
   case V_IF:
531✔
1110
      vlog_check_if(v);
531✔
1111
      break;
531✔
1112
   case V_IF_GENERATE:
25✔
1113
      vlog_check_if_generate(v);
25✔
1114
      break;
25✔
1115
   case V_COND:
645✔
1116
      vlog_check_cond(v);
645✔
1117
      break;
645✔
1118
   case V_CASE:
8✔
1119
      vlog_check_case(v);
8✔
1120
      break;
8✔
1121
   case V_FOR_LOOP:
48✔
1122
   case V_FOR_GENERATE:
1123
      vlog_check_for_loop(v);
48✔
1124
      break;
48✔
1125
   case V_REPEAT:
3✔
1126
      vlog_check_repeat(v);
3✔
1127
      break;
3✔
1128
   case V_WHILE:
3✔
1129
      vlog_check_while(v);
3✔
1130
      break;
3✔
1131
   case V_BASSIGN:
1,157✔
1132
      vlog_check_bassign(v);
1,157✔
1133
      break;
1,157✔
1134
   case V_NBASSIGN:
124✔
1135
      vlog_check_nbassign(v);
124✔
1136
      break;
124✔
1137
   case V_RETURN:
5✔
1138
      vlog_check_return(v);
5✔
1139
      break;
5✔
1140
   case V_SYS_TCALL:
1,127✔
1141
      vlog_check_sys_tcall(v);
1,127✔
1142
      break;
1,127✔
1143
   case V_USER_TCALL:
3✔
1144
      vlog_check_user_tcall(v);
3✔
1145
      break;
3✔
1146
   case V_SUPER_CALL:
1147
      vlog_check_super_call(v);
1148
      break;
1149
   case V_VOID_CALL:
1✔
1150
      vlog_check_expr(vlog_value(v));
1✔
1151
      break;
1✔
1152
   case V_EVENT_CONTROL:
147✔
1153
      vlog_check_event_control(v);
147✔
1154
      break;
147✔
1155
   case V_DELAY_CONTROL:
547✔
1156
      vlog_check_delay_control(v);
547✔
1157
      break;
547✔
1158
   case V_GATE_INST:
36✔
1159
      vlog_check_gate_inst(v);
36✔
1160
      break;
36✔
1161
   case V_ASSIGN:
214✔
1162
      vlog_check_assign(v);
214✔
1163
      break;
214✔
1164
   case V_OP_ASSIGN:
75✔
1165
      vlog_check_op_assign(v);
75✔
1166
      break;
75✔
1167
   case V_PREFIX:
12✔
1168
   case V_POSTFIX:
1169
      vlog_check_prefix_postfix(v);
12✔
1170
      break;
12✔
1171
   case V_DEASSIGN:
×
1172
      vlog_check_deassign(v);
×
1173
      break;
×
1174
   case V_PRIMITIVE:
18✔
1175
      vlog_check_primitive(v);
18✔
1176
      break;
18✔
1177
   case V_DATA_TYPE:
883✔
1178
      vlog_check_data_type(v);
883✔
1179
      break;
883✔
1180
   case V_DIMENSION:
417✔
1181
      vlog_check_dimension(v);
417✔
1182
      break;
417✔
1183
   case V_WAIT:
1184
      vlog_check_wait(v);
1185
      break;
1186
   case V_SPECIFY:
1187
   case V_IMPORT_DECL:
1188
      break;
1189
   default:
×
1190
      CANNOT_HANDLE(v);
×
1191
   }
1192
}
9,767✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc