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

aas-core-works / aas-core-codegen / 28199159015

25 Jun 2026 08:41PM UTC coverage: 84.246% (+0.05%) from 84.197%
28199159015

push

github

web-flow
Introduce lists of constrained primitives (#656)

We implemented the support for lists of constrained primitives when we
introduced the lists of primitives, but we didn't test it.

In this change, we thoroughly test the lists of constrained primitives
and fix the generators where necessary.

6 of 7 new or added lines in 3 files covered. (85.71%)

1 existing line in 1 file now uncovered.

30391 of 36074 relevant lines covered (84.25%)

2.53 hits per line

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

90.84
/aas_core_codegen/cpp/lib/_generate_verification.py
1
"""Generate code of verification logic."""
2
import io
3✔
3
from typing import (
3✔
4
    Optional,
5
    List,
6
    Tuple,
7
    Union,
8
    Sequence,
9
    Mapping,
10
    Final,
11
)
12

13
from icontract import ensure, require
3✔
14

15
from aas_core_codegen import intermediate
3✔
16
from aas_core_codegen import specific_implementations
3✔
17
from aas_core_codegen.common import (
3✔
18
    Error,
19
    Identifier,
20
    assert_never,
21
    Stripped,
22
    indent_but_first_line,
23
    wrap_text_into_lines,
24
)
25
from aas_core_codegen.cpp import (
3✔
26
    common as cpp_common,
27
    naming as cpp_naming,
28
    description as cpp_description,
29
    transpilation as cpp_transpilation,
30
    optionaling as cpp_optionaling,
31
    yielding as cpp_yielding,
32
)
33
from aas_core_codegen.cpp.common import (
3✔
34
    INDENT as I,
35
    INDENT2 as II,
36
    INDENT3 as III,
37
    INDENT4 as IIII,
38
)
39
from aas_core_codegen.intermediate import type_inference as intermediate_type_inference
3✔
40
from aas_core_codegen.parse import tree as parse_tree
3✔
41
from aas_core_codegen.yielding import flow as yielding_flow
3✔
42

43

44
@ensure(lambda result: (result[0] is not None) ^ (result[1] is not None))
3✔
45
def _generate_verification_function_definition(
3✔
46
    verification: Union[
47
        intermediate.ImplementationSpecificVerification,
48
        intermediate.TranspilableVerification,
49
        intermediate.PatternVerification,
50
    ],
51
    spec_impls: specific_implementations.SpecificImplementations,
52
) -> Tuple[Optional[Stripped], Optional[Error]]:
53
    """Generate the definition of a verification functions."""
54
    if isinstance(verification, intermediate.ImplementationSpecificVerification):
3✔
55
        implementation_key = specific_implementations.ImplementationKey(
3✔
56
            f"verification/{verification.name}.hpp"
57
        )
58

59
        code = spec_impls.get(implementation_key, None)
3✔
60

61
        if code is None:
3✔
62
            return None, Error(
×
63
                verification.parsed.node,
64
                f"The header snippet is missing for "
65
                f"the implementation-specific verification "
66
                f"function: {implementation_key}",
67
            )
68

69
        return code, None
3✔
70

71
    arg_types_names = [
3✔
72
        (
73
            cpp_common.generate_type_with_const_ref_if_applicable(
74
                type_annotation=arg.type_annotation,
75
                types_namespace=cpp_common.TYPES_NAMESPACE,
76
            ),
77
            cpp_naming.argument_name(arg.name),
78
        )
79
        for arg in verification.arguments
80
    ]
81

82
    function_name = cpp_naming.function_name(verification.name)
3✔
83
    arg_definitions_joined = ",\n".join(
3✔
84
        f"{arg_type} {arg_name}" for arg_type, arg_name in arg_types_names
85
    )
86

87
    blocks = []  # type: List[Stripped]
3✔
88
    if verification.description is not None:
3✔
89
        comment, errors = cpp_description.generate_comment_for_summary_remarks(
3✔
90
            description=verification.description,
91
            context=cpp_description.Context(
92
                namespace=cpp_common.VERIFICATION_NAMESPACE, cls_or_enum=None
93
            ),
94
        )
95
        if errors is not None:
3✔
96
            return None, Error(
×
97
                verification.parsed.node,
98
                f"Failed to generate the description for "
99
                f"verification function {verification.name!r}",
100
                errors,
101
            )
102
        assert comment is not None
3✔
103
        blocks.append(comment)
3✔
104

105
    blocks.append(
3✔
106
        Stripped(
107
            f"""\
108
bool {function_name}(
109
{I}{indent_but_first_line(arg_definitions_joined, I)}
110
);"""
111
        )
112
    )
113

114
    return Stripped("\n".join(blocks)), None
3✔
115

116

117
def _constrained_primitive_verificator_value_is_pointer(
3✔
118
    primitive_type: intermediate.PrimitiveType,
119
) -> bool:
120
    """
121
    Check whether we keep the value of a constrained primitive as a pointer.
122

123
    Values which are cheap to copy such as booleans and integers are copied by value
124
    in the verificator constructor. On the other hand, primitive types represented as
125
    STL containers are copied as pointers to avoid unnecessary cost.
126

127
    In many places in code we have to decide how to dereference the value.
128
    """
129
    if primitive_type is intermediate.PrimitiveType.BOOL:
3✔
130
        return False
3✔
131

132
    elif primitive_type is intermediate.PrimitiveType.INT:
3✔
133
        return False
3✔
134

135
    elif primitive_type is intermediate.PrimitiveType.FLOAT:
3✔
136
        return False
3✔
137

138
    elif primitive_type is intermediate.PrimitiveType.STR:
3✔
139
        return True
3✔
140

141
    elif primitive_type is intermediate.PrimitiveType.BYTEARRAY:
3✔
142
        return True
3✔
143

144
    else:
145
        assert_never(primitive_type)
×
146

147

148
def _generate_definition_of_verify_constrained_primitive(
3✔
149
    constrained_primitive: intermediate.ConstrainedPrimitive,
150
) -> Stripped:
151
    """Generate the def. of a verification function for the constrained primitive."""
152
    verify_name = cpp_naming.function_name(
3✔
153
        Identifier(f"verify_{constrained_primitive.name}")
154
    )
155

156
    arg_type = cpp_common.generate_primitive_type_with_const_ref_if_applicable(
3✔
157
        constrained_primitive.constrainee
158
    )
159

160
    arg_name = cpp_naming.argument_name(Identifier("that"))
3✔
161

162
    if _constrained_primitive_verificator_value_is_pointer(
3✔
163
        primitive_type=constrained_primitive.constrainee
164
    ):
165
        documentation_comment = Stripped(
3✔
166
            """\
167
/**
168
 * \\brief Verify that the invariants hold for \\p that value.
169
 *
170
 * The \\p that value should outlive the verification.
171
 *
172
 * \\param that value to be verified
173
 * \\return Iterable over constraint violations
174
 */"""
175
        )
176
    else:
177
        documentation_comment = Stripped(
3✔
178
            """\
179
/**
180
 * \\brief Verify that the invariants hold for \\p that value.
181
 *
182
 * \\param that value to be verified
183
 * \\return Iterable over constraint violations
184
 */"""
185
        )
186

187
    return Stripped(
3✔
188
        f"""\
189
{documentation_comment}
190
std::unique_ptr<IVerification> {verify_name}(
191
{I}{arg_type} {arg_name}
192
);"""
193
    )
194

195

196
# fmt: off
197
@ensure(lambda result: (result[0] is not None) ^ (result[1] is not None))
3✔
198
@ensure(
3✔
199
    lambda result:
200
    not (result[0] is not None) or result[0].endswith('\n'),
201
    "Trailing newline mandatory for valid end-of-files"
202
)
203
# fmt: on
204
def generate_header(
3✔
205
    symbol_table: intermediate.SymbolTable,
206
    spec_impls: specific_implementations.SpecificImplementations,
207
    library_namespace: Stripped,
208
) -> Tuple[Optional[str], Optional[List[Error]]]:
209
    """Generate header of verification logic."""
210
    namespace = Stripped(f"{library_namespace}::verification")
3✔
211

212
    include_guard_var = cpp_common.include_guard_var(namespace)
3✔
213

214
    include_prefix_path = cpp_common.generate_include_prefix_path(library_namespace)
3✔
215

216
    blocks = [
3✔
217
        Stripped(
218
            f"""\
219
#ifndef {include_guard_var}
220
#define {include_guard_var}"""
221
        ),
222
        cpp_common.WARNING,
223
        Stripped(
224
            f"""\
225
#include "{include_prefix_path}/common.hpp"
226
#include "{include_prefix_path}/iteration.hpp"
227
#include "{include_prefix_path}/pattern.hpp"
228
#include "{include_prefix_path}/types.hpp"
229

230
#pragma warning(push, 0)
231
#include <set>
232
#pragma warning(pop)"""
233
        ),
234
        cpp_common.generate_namespace_opening(library_namespace),
235
        Stripped(
236
            """\
237
/**
238
 * \\defgroup verification Verify that instances conform to the meta-model constraints.
239
 * @{
240
 */
241
namespace verification {"""
242
        ),
243
        Stripped(
244
            """\
245
// region Forward declarations
246
class Iterator;
247
class IVerification;
248

249
namespace impl {
250
class IVerificator;
251
}  // namespace impl
252
// endregion Forward declarations"""
253
        ),
254
        Stripped(
255
            f"""\
256
/**
257
 * Represent a verification error in an instance.
258
 */
259
struct Error {{
260
{I}/**
261
{I} * Human-readable description of the error
262
{I} */
263
{I}std::wstring cause;
264

265
{I}/**
266
{I} * Path to the erroneous value
267
{I} */
268
{I}iteration::Path path;
269

270
{I}explicit Error(std::wstring a_cause);
271
{I}Error(std::wstring a_cause, iteration::Path a_path);
272
}};  // struct Error"""
273
        ),
274
        Stripped(
275
            f"""\
276
/**
277
 * \\brief Iterate over the verification errors.
278
 *
279
 * The user is expected to take ownership of the errors if they need to be further
280
 * processed.
281
 *
282
 * Unlike STL, this is <em>not</em> a light-weight iterator. We implement
283
 * a "yielding" iterator by leveraging code generation so that we always keep
284
 * the model stack as well as the properties verified thus far.
285
 *
286
 * This means that copy-construction and equality comparisons are much more heavy-weight
287
 * than you'd usually expect from an STL iterator. For example, if you want to sort
288
 * the errors by some criterion, you are most probably faster if you populate a vector,
289
 * and then sort the vector.
290
 *
291
 * Also, given that this iterator is not light-weight, you should in almost all cases
292
 * avoid the postfix increment (it++) and prefer the prefix one (++it) as the postfix
293
 * increment would create an iterator copy every time.
294
 *
295
 * We follow the C++ standard, and assume that comparison between the two iterators
296
 * over two different collections results in undefined behavior. See
297
 * http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2009/n2948.html and
298
 * https://stackoverflow.com/questions/4657513/comparing-iterators-from-different-containers.
299
 */
300
class Iterator {{
301
{I}using iterator_category = std::forward_iterator_tag;
302
{I}/// The difference is meaningless, but has to be defined.
303
{I}using difference_type = std::ptrdiff_t;
304
{I}using value_type = Error;
305
{I}using pointer = const Error*;
306
{I}using reference = const Error&;
307

308
 public:
309
{I}explicit Iterator(
310
{II}std::unique_ptr<impl::IVerificator> verificator
311
{I}) :
312
{I}verificator_(std::move(verificator)) {{
313
{II}  // Intentionally empty.
314
{I}}}
315

316
{I}Iterator(const Iterator& other);
317
{I}Iterator(Iterator&& other);
318

319
{I}Iterator& operator=(const Iterator& other);
320
{I}Iterator& operator=(Iterator&& other);
321

322
{I}reference operator*() const;
323
{I}pointer operator->() const;
324

325
{I}// Prefix increment
326
{I}Iterator& operator++();
327

328
{I}// Postfix increment
329
{I}Iterator operator++(int);
330

331
{I}friend bool operator==(const Iterator& a, const Iterator& b);
332
{I}friend bool operator!=(const Iterator& a, const Iterator& b);
333

334
 private:
335
{I}std::unique_ptr<impl::IVerificator> verificator_;
336
}};"""
337
        ),
338
        Stripped("bool operator==(const Iterator& a, const Iterator& b);"),
339
        Stripped("bool operator!=(const Iterator& a, const Iterator& b);"),
340
        Stripped(
341
            f"""\
342
/// \\cond HIDDEN
343
namespace impl {{
344
class IVerificator {{
345
 public:
346
{I}virtual void Start() = 0;
347
{I}virtual void Next() = 0;
348
{I}virtual bool Done() const = 0;
349

350
{I}virtual const Error& Get() const = 0;
351
{I}virtual Error& GetMutable() = 0;
352
{I}virtual long Index() const = 0;
353

354
{I}virtual std::unique_ptr<IVerificator> Clone() const = 0;
355

356
{I}virtual ~IVerificator() = default;
357
}};  // class IVerificator
358
}}  // namespace impl
359
/// \\endcond"""
360
        ),
361
        Stripped(
362
            f"""\
363
class IVerification {{
364
 public:
365
{I}virtual Iterator begin() const = 0;
366
{I}virtual const Iterator& end() const = 0;
367
{I}virtual ~IVerification() = default;
368
}};  // class IVerification"""
369
        ),
370
        Stripped(
371
            f"""\
372
/**
373
 * \\brief Verify that the instance conforms to the meta-model constraints.
374
 *
375
 * Do not proceed to verify the instances referenced from
376
 * the given instance.
377
 *
378
 * Range-based loops should fit the vast majority of the use cases:
379
 * \\code
380
 * std::shared_ptr<types::Environment> env = ...;
381
 * for (const Error& error : NonRecursiveVerification(env)) {{
382
 * {I}report_somehow(error);
383
 * }}
384
 * \\endcode
385
 *
386
 * We use const references to shared pointers here for efficiency. Since
387
 * we do not make a copy of \\p that shared pointer, it is very important that
388
 * the given shared pointer outlives the verification, lest cause undefined behavior.
389
 * See these StackOverflow questions:
390
 * * https://stackoverflow.com/questions/12002480/passing-stdshared-ptr-to-constructors/12002668#12002668
391
 * * https://stackoverflow.com/questions/3310737/should-we-pass-a-shared-ptr-by-reference-or-by-value
392
 * * https://stackoverflow.com/questions/37610494/passing-const-shared-ptrt-versus-just-shared-ptrt-as-parameter
393
 */
394
class NonRecursiveVerification : public IVerification {{
395
 public:
396
{I}NonRecursiveVerification(
397
{II}const std::shared_ptr<types::IClass>& instance
398
{I});
399

400
{I}Iterator begin() const override;
401
{I}const Iterator& end() const override;
402

403
{I}~NonRecursiveVerification() override = default;
404
 private:
405
{I}const std::shared_ptr<types::IClass>& instance_;
406
}};  // class NonRecursiveVerification"""
407
        ),
408
        Stripped(
409
            f"""\
410
/**
411
 * \\brief Verify that the instance conforms to the meta-model constraints.
412
 *
413
 * Also verify recursively all the instances referenced from
414
 * the given instance.
415
 *
416
 * Range-based loops should fit the vast majority of the use cases:
417
 * \\code
418
 * std::shared_ptr<types::Environment> env = ...;
419
 * for (const Error& error : RecursiveVerification(env)) {{
420
 * {I}report_somehow(error);
421
 * }}
422
 * \\endcode
423
 *
424
 * We use const references to shared pointers here for efficiency. Since
425
 * we do not make a copy of \\p that shared pointer, it is very important that
426
 * the given shared pointer outlives the verification, lest cause undefined behavior.
427
 * See these StackOverflow questions:
428
 * * https://stackoverflow.com/questions/12002480/passing-stdshared-ptr-to-constructors/12002668#12002668
429
 * * https://stackoverflow.com/questions/3310737/should-we-pass-a-shared-ptr-by-reference-or-by-value
430
 * * https://stackoverflow.com/questions/37610494/passing-const-shared-ptrt-versus-just-shared-ptrt-as-parameter
431
 */
432
class RecursiveVerification : public IVerification {{
433
 public:
434
{I}RecursiveVerification(
435
{II}const std::shared_ptr<types::IClass>& instance
436
{I});
437

438
{I}Iterator begin() const override;
439
{I}const Iterator& end() const override;
440

441
{I}~RecursiveVerification() override = default;
442
 private:
443
{I}const std::shared_ptr<types::IClass>& instance_;
444
}};  // class RecursiveVerification"""
445
        ),
446
    ]  # type: List[Stripped]
447

448
    errors = []  # type: List[Error]
3✔
449

450
    if len(symbol_table.verification_functions) > 0:
3✔
451
        blocks.append(Stripped("// region Verification functions"))
3✔
452

453
        for verification in symbol_table.verification_functions:
3✔
454
            block, error = _generate_verification_function_definition(
3✔
455
                verification=verification, spec_impls=spec_impls
456
            )
457
            if error is not None:
3✔
458
                errors.append(error)
×
459
                continue
×
460
            else:
461
                assert block is not None
3✔
462
                blocks.append(block)
3✔
463

464
        blocks.append(Stripped("// endregion Verification functions"))
3✔
465

466
    if len(symbol_table.constrained_primitives) > 0:
3✔
467
        blocks.append(Stripped("// region Verification of constrained primitives"))
3✔
468

469
        for constrained_primitive in symbol_table.constrained_primitives:
3✔
470
            blocks.append(
3✔
471
                _generate_definition_of_verify_constrained_primitive(
472
                    constrained_primitive=constrained_primitive
473
                )
474
            )
475

476
        blocks.append(Stripped("// endregion Verification of constrained primitives"))
3✔
477

478
    if len(errors) > 0:
3✔
479
        return None, errors
×
480

481
    blocks.extend(
3✔
482
        [
483
            Stripped(
484
                """\
485
}  // namespace verification
486
/**@}*/"""
487
            ),
488
            cpp_common.generate_namespace_closing(library_namespace),
489
            cpp_common.WARNING,
490
            Stripped(f"#endif  // {include_guard_var}"),
491
        ]
492
    )
493

494
    out = io.StringIO()
3✔
495
    for i, block in enumerate(blocks):
3✔
496
        if i > 0:
3✔
497
            out.write("\n\n")
3✔
498

499
        out.write(block)
3✔
500

501
    out.write("\n")
3✔
502

503
    return out.getvalue(), None
3✔
504

505

506
def _generate_error_implementation() -> List[Stripped]:
3✔
507
    """Generate the implementation of the ``Error`` struct."""
508
    return [
3✔
509
        Stripped("// region struct Error"),
510
        Stripped(
511
            f"""\
512
Error::Error(
513
{I}std::wstring a_cause
514
) :
515
{I}cause(std::move(a_cause)) {{
516
{I}// Intentionally empty.
517
}}"""
518
        ),
519
        Stripped(
520
            f"""\
521
Error::Error(
522
{I}std::wstring a_cause,
523
{I}iteration::Path a_path
524
) :
525
{I}cause(std::move(a_cause)),
526
{I}path(std::move(a_path)) {{
527
{I}// Intentionally empty.
528
}}"""
529
        ),
530
        Stripped("// endregion struct Error"),
531
    ]
532

533

534
def _generate_new_non_recursive_verificator_definition() -> Stripped:
3✔
535
    """Generate the def. of the factory function for non-recursive verificators."""
536
    new_non_recursive_verificator = cpp_naming.function_name(
3✔
537
        Identifier("new_non_recursive_verificator")
538
    )
539

540
    return Stripped(
3✔
541
        f"""\
542
/**
543
 * Produce a non-recursive verificator of the instance given its runtime model type.
544
 */
545
std::unique_ptr<impl::IVerificator> {new_non_recursive_verificator}(
546
{I}const std::shared_ptr<types::IClass>& instance
547
);"""
548
    )
549

550

551
def _generate_iterator_implementation() -> List[Stripped]:
3✔
552
    """Generate the implementation of the class ``Iterator``."""
553
    return [
3✔
554
        Stripped("// region struct Iterator"),
555
        Stripped(
556
            f"""\
557
Iterator::Iterator(
558
{I}const Iterator& other
559
) :
560
{I}verificator_(other.verificator_->Clone()) {{
561
{I}// Intentionally empty.
562
}}"""
563
        ),
564
        Stripped(
565
            f"""\
566
Iterator::Iterator(
567
{I}Iterator&& other
568
) :
569
{I}verificator_(std::move(other.verificator_)) {{
570
{I}// Intentionally empty.
571
}}"""
572
        ),
573
        Stripped(
574
            f"""\
575
Iterator& Iterator::operator=(const Iterator& other) {{
576
{I}return *this = Iterator(other);
577
}}"""
578
        ),
579
        Stripped(
580
            f"""\
581
Iterator& Iterator::operator=(Iterator&& other) {{
582
{I}if (this != &other) {{
583
{II}verificator_ = std::move(other.verificator_);
584
{I}}}
585

586
{I}return *this;
587
}}"""
588
        ),
589
        Stripped(
590
            f"""\
591
const Error& Iterator::operator*() const {{
592
{I}if (verificator_->Done()) {{
593
{II}throw std::logic_error(
594
{III}"You want to de-reference from a completed iterator "
595
{III}"over verification errors."
596
{II});
597
{I}}}
598

599
{I}return verificator_->Get();
600
}}"""
601
        ),
602
        Stripped(
603
            f"""\
604
const Error* Iterator::operator->() const {{
605
{I}if (verificator_->Done()) {{
606
{II}throw std::logic_error(
607
{III}"You want to de-reference from a completed iterator "
608
{III}"over verification errors."
609
{II});
610
{I}}}
611

612
{I}return &(verificator_->Get());
613
}}"""
614
        ),
615
        Stripped(
616
            f"""\
617
// Prefix increment
618
Iterator& Iterator::operator++() {{
619
{I}if (verificator_->Done()) {{
620
{II}throw std::logic_error(
621
{III}"You want to move a completed iterator "
622
{III}"over verification errors."
623
{II});
624
{I}}}
625

626
{I}verificator_->Next();
627
{I}return *this;
628
}}"""
629
        ),
630
        Stripped(
631
            f"""\
632
// Postfix increment
633
Iterator Iterator::operator++(int) {{
634
{I}Iterator result(*this);
635
{I}++(*this);
636
{I}return result;
637
}}"""
638
        ),
639
        Stripped(
640
            f"""\
641
bool operator==(const Iterator& a, const Iterator& b) {{
642
{I}return a.verificator_->Index() == b.verificator_->Index();
643
}}"""
644
        ),
645
        Stripped(
646
            f"""\
647
bool operator!=(const Iterator& a, const Iterator& b) {{
648
{I}return a.verificator_->Index() != b.verificator_->Index();
649
}}"""
650
        ),
651
        Stripped("// endregion struct Iterator"),
652
    ]
653

654

655
def _generate_non_recursive_verification() -> List[Stripped]:
3✔
656
    """Generate the ``NonRecursiveVerification`` class."""
657
    return [
3✔
658
        Stripped("// region NonRecursiveVerification"),
659
        Stripped(
660
            f"""\
661
NonRecursiveVerification::NonRecursiveVerification(
662
{I}const std::shared_ptr<types::IClass>& instance
663
) : instance_(instance) {{
664
{I}// Intentionally empty.
665
}}"""
666
        ),
667
        Stripped(
668
            f"""\
669
Iterator NonRecursiveVerification::begin() const {{
670
{I}std::unique_ptr<impl::IVerificator> verificator(
671
{II}NewNonRecursiveVerificator(instance_)
672
{I});
673

674
{I}verificator->Start();
675

676
{I}// NOTE(mristin):
677
{I}// We short-circuit here for efficiency, as we can immediately dispose
678
{I}// of the verificator.
679
{I}if (verificator->Done()) {{
680
{II}return Iterator(common::make_unique<AlwaysDoneVerificator>());
681
{I}}}
682

683
{I}return Iterator(std::move(verificator));
684
}}"""
685
        ),
686
        Stripped(
687
            f"""\
688
const Iterator& NonRecursiveVerification::end() const {{
689
{I}static Iterator iterator(common::make_unique<AlwaysDoneVerificator>());
690
{I}return iterator;
691
}}"""
692
        ),
693
        Stripped("// endregion NonRecursiveVerification"),
694
    ]
695

696

697
def _generate_recursive_verification() -> List[Stripped]:
3✔
698
    """Generate the ``RecursiveVerification`` class."""
699
    return [
3✔
700
        Stripped("// region RecursiveVerification"),
701
        Stripped(
702
            f"""\
703
RecursiveVerification::RecursiveVerification(
704
{I}const std::shared_ptr<types::IClass>& instance
705
) : instance_(instance) {{
706
{I}// Intentionally empty.
707
}}"""
708
        ),
709
        Stripped(
710
            f"""\
711
Iterator RecursiveVerification::begin() const {{
712
{I}std::unique_ptr<impl::IVerificator> verificator(
713
{II}common::make_unique<RecursiveVerificator>(instance_)
714
{I});
715

716
{I}verificator->Start();
717

718
{I}// NOTE(mristin):
719
{I}// We short-circuit here for efficiency, as we can immediately dispose
720
{I}// of the verificator.
721
{I}if (verificator->Done()) {{
722
{II}return Iterator(common::make_unique<AlwaysDoneVerificator>());
723
{I}}}
724

725
{I}return Iterator(std::move(verificator));
726
}}"""
727
        ),
728
        Stripped(
729
            f"""\
730
const Iterator& RecursiveVerification::end() const {{
731
{I}static Iterator iterator(common::make_unique<AlwaysDoneVerificator>());
732
{I}return iterator;
733
}}"""
734
        ),
735
        Stripped("// endregion RecursiveVerification"),
736
    ]
737

738

739
def _generate_always_done_verificator() -> List[Stripped]:
3✔
740
    """Generate the verificator which always has ``Done`` set."""
741
    return [
3✔
742
        Stripped("// region class AlwaysDoneVerificator"),
743
        Stripped(
744
            f"""\
745
class AlwaysDoneVerificator : public impl::IVerificator {{
746
 public:
747
{I}void Start() override;
748
{I}void Next() override;
749
{I}bool Done() const override;
750
{I}const Error& Get() const override;
751
{I}Error& GetMutable() override;
752
{I}long Index() const override;
753
{I}std::unique_ptr<impl::IVerificator> Clone() const override;
754

755
{I}virtual ~AlwaysDoneVerificator() = default;
756
}};  // class AlwaysDoneVerificator"""
757
        ),
758
        Stripped(
759
            f"""\
760
void AlwaysDoneVerificator::Start() {{
761
{I}// Intentionally empty.
762
}}"""
763
        ),
764
        Stripped(
765
            f"""\
766
void AlwaysDoneVerificator::Next() {{
767
{I}throw std::logic_error(
768
{II}"You want to move an AlwaysDoneVerificator, "
769
{II}"but the verificator is always done, as its name suggests."
770
{I});
771
}}"""
772
        ),
773
        Stripped(
774
            f"""\
775
bool AlwaysDoneVerificator::Done() const {{
776
{I}return true;
777
}}"""
778
        ),
779
        Stripped(
780
            f"""\
781
const Error& AlwaysDoneVerificator::Get() const {{
782
{II}throw std::logic_error(
783
{III}"You want to get from an AlwaysDoneVerificator, "
784
{III}"but the verificator is always done, as its name suggests."
785
{II});
786
}}"""
787
        ),
788
        Stripped(
789
            f"""\
790
Error& AlwaysDoneVerificator::GetMutable() {{
791
{II}throw std::logic_error(
792
{III}"You want to get mutable from an AlwaysDoneVerificator, "
793
{III}"but the verificator is always done, as its name suggests."
794
{II});
795
}}"""
796
        ),
797
        Stripped(
798
            f"""\
799
long AlwaysDoneVerificator::Index() const {{
800
{I}return -1;
801
}}"""
802
        ),
803
        Stripped(
804
            f"""\
805
std::unique_ptr<impl::IVerificator> AlwaysDoneVerificator::Clone() const {{
806
{I}return common::make_unique<AlwaysDoneVerificator>(*this);
807
}}"""
808
        ),
809
        Stripped("// endregion class AlwaysDoneVerificator"),
810
    ]
811

812

813
def _find_constrained_primitive(
3✔
814
    type_annotation: intermediate.TypeAnnotationUnion,
815
) -> Optional[intermediate.ConstrainedPrimitive]:
816
    """
817
    Find the constrained primitive in the given type annotation.
818

819
    The constrained primitive can be referenced from the type annotation, or contained
820
    as its generic parameter.
821
    """
822
    if isinstance(type_annotation, intermediate.PrimitiveTypeAnnotation):
3✔
823
        return None
3✔
824

825
    elif isinstance(type_annotation, intermediate.OurTypeAnnotation):
3✔
826
        if isinstance(type_annotation.our_type, intermediate.ConstrainedPrimitive):
3✔
827
            return type_annotation.our_type
3✔
828

829
        else:
830
            return None
3✔
831

832
    elif isinstance(type_annotation, intermediate.OptionalTypeAnnotation):
3✔
833
        return _find_constrained_primitive(type_annotation.value)
3✔
834

835
    elif isinstance(type_annotation, intermediate.ListTypeAnnotation):
3✔
836
        return _find_constrained_primitive(type_annotation.items)
3✔
837

838
    else:
839
        assert_never(type_annotation)
×
840

841

842
class VerificatorQualities:
3✔
843
    """Model the relevant qualities of a verificator."""
844

845
    #: Class that we want to verify
846
    cls: Final[intermediate.ConcreteClass]
3✔
847

848
    #: If set, the verificator performs no verification steps.
849
    is_noop: Final[bool]
3✔
850

851
    #: List properties which are annotated with a (possibly optional) constrained
852
    #: primitive
853
    constrained_primitive_properties: Final[Sequence[intermediate.Property]]
3✔
854

855
    # fmt: off
856
    @ensure(
3✔
857
        lambda self:
858
        not (
859
            len(self.cls.invariants) == 0
860
            and len(self.constrained_primitive_properties) == 0
861
        ) or self.is_noop,
862
        "The verificator is a no-op if there are no invariants and no constrained "
863
        "primitive properties in the class"
864
    )
865
    @ensure(
3✔
866
        lambda self:
867
        not (
868
            len(self.cls.invariants) > 0
869
            or len(self.constrained_primitive_properties) > 0
870
        ) or not self.is_noop,
871
        "The verificator is *not* a no-op if there is at least one invariant or "
872
        "a property annotated with a constrained primitive"
873
    )
874
    # fmt: on
875
    def __init__(self, cls: intermediate.ConcreteClass) -> None:
3✔
876
        self.cls = cls
3✔
877

878
        self.constrained_primitive_properties = [
3✔
879
            prop
880
            for prop in cls.properties
881
            if _find_constrained_primitive(prop.type_annotation) is not None
882
        ]
883

884
        self.is_noop = (
3✔
885
            len(cls.invariants) == 0 and len(self.constrained_primitive_properties) == 0
886
        )
887

888

889
@require(lambda verificator_qualities: verificator_qualities.is_noop)
3✔
890
def _generate_empty_non_recursive_verificator(
3✔
891
    verificator_qualities: VerificatorQualities,
892
) -> List[Stripped]:
893
    """
894
    Generate an implementation of a non-recursive verificator which is always done.
895

896
    Though the implementation is a duplicate in logic of ``AlwaysDoneVerificator``,
897
    the assertion error messages are different, so we generate a separate class.
898
    """
899
    # Shortcut
900
    cls = verificator_qualities.cls
3✔
901

902
    of_cls = cpp_naming.class_name(Identifier(f"Of_{cls.name}"))
3✔
903
    interface_name = cpp_naming.interface_name(cls.name)
3✔
904

905
    return [
3✔
906
        Stripped(
907
            f"""\
908
class {of_cls} : public impl::IVerificator {{
909
 public:
910
{I}{of_cls}(
911
{II}const std::shared_ptr<types::IClass>& instance
912
{I});
913

914
{I}void Start() override;
915
{I}void Next() override;
916
{I}bool Done() const override;
917
{I}const Error& Get() const override;
918
{I}Error& GetMutable() override;
919
{I}long Index() const override;
920

921
{I}std::unique_ptr<impl::IVerificator> Clone() const override;
922

923
{I}~{of_cls}() override = default;
924
}};  // class {of_cls}"""
925
        ),
926
        Stripped(
927
            f"""\
928
{of_cls}::{of_cls}(
929
{I}const std::shared_ptr<types::IClass>&
930
) {{
931
{I}// Intentionally empty.
932
}}"""
933
        ),
934
        Stripped(
935
            f"""\
936
void {of_cls}::Start() {{
937
{I}// Intentionally empty.
938
}}"""
939
        ),
940
        Stripped(
941
            f"""\
942
void {of_cls}::Next() {{
943
{I}throw std::logic_error(
944
{II}"You want to move "
945
{II}"a verificator {of_cls}, "
946
{II}"but the verificator is always done as "
947
{II}"{interface_name} "
948
{II}"has no invariants defined."
949
{I});
950
}}"""
951
        ),
952
        Stripped(
953
            f"""\
954
bool {of_cls}::Done() const {{
955
{I}return true;
956
}}"""
957
        ),
958
        Stripped(
959
            f"""\
960
const Error& {of_cls}::Get() const {{
961
{I}throw std::logic_error(
962
{II}"You want to get from "
963
{II}"a verificator {of_cls}, "
964
{II}"but the verificator is always done as "
965
{II}"{interface_name} "
966
{II}"has no invariants defined."
967
{I});
968
}}"""
969
        ),
970
        Stripped(
971
            f"""\
972
Error& {of_cls}::GetMutable() {{
973
{I}throw std::logic_error(
974
{II}"You want to get mutable from "
975
{II}"a verificator {of_cls}, "
976
{II}"but the verificator is always done as "
977
{II}"{interface_name} "
978
{II}"has no invariants defined."
979
{I});
980
}}"""
981
        ),
982
        Stripped(
983
            f"""\
984
long {of_cls}::Index() const {{
985
{I}return -1;
986
}}"""
987
        ),
988
        Stripped(
989
            f"""\
990
std::unique_ptr<impl::IVerificator> {of_cls}::Clone() const {{
991
{I}return common::make_unique<
992
{II}{of_cls}
993
{I}>(*this);
994
}}"""
995
        ),
996
    ]
997

998

999
class _ClassInvariantTranspiler(cpp_transpilation.Transpiler):
3✔
1000
    """Transpile invariants of the classes."""
1001

1002
    def __init__(
3✔
1003
        self,
1004
        type_map: Mapping[
1005
            parse_tree.Node, intermediate_type_inference.TypeAnnotationUnion
1006
        ],
1007
        is_optional_map: Mapping[parse_tree.Node, bool],
1008
        environment: intermediate_type_inference.Environment,
1009
        symbol_table: intermediate.SymbolTable,
1010
    ) -> None:
1011
        """Initialize with the given values."""
1012
        cpp_transpilation.Transpiler.__init__(
3✔
1013
            self,
1014
            type_map=type_map,
1015
            is_optional_map=is_optional_map,
1016
            environment=environment,
1017
            types_namespace=cpp_common.TYPES_NAMESPACE,
1018
        )
1019

1020
        self._symbol_table = symbol_table
3✔
1021

1022
    def transform_name(
3✔
1023
        self, node: parse_tree.Name
1024
    ) -> Tuple[Optional[Stripped], Optional[Error]]:
1025
        if node.identifier in self._variable_name_set:
3✔
1026
            return Stripped(cpp_naming.variable_name(node.identifier)), None
3✔
1027

1028
        if node.identifier == "self":
3✔
1029
            # The ``instance_`` refers to the instance under verification.
1030
            return Stripped("instance_"), None
3✔
1031

1032
        if node.identifier in self._symbol_table.constants_by_name:
3✔
1033
            constant = cpp_naming.constant_name(node.identifier)
3✔
1034
            return Stripped(f"{cpp_common.CONSTANTS_NAMESPACE}::{constant}"), None
3✔
1035

1036
        if node.identifier in self._symbol_table.verification_functions_by_name:
3✔
1037
            return Stripped(cpp_naming.function_name(node.identifier)), None
3✔
1038

1039
        our_type = self._symbol_table.find_our_type(name=node.identifier)
3✔
1040
        if isinstance(our_type, intermediate.Enumeration):
3✔
1041
            return (
3✔
1042
                Stripped(
1043
                    f"{cpp_common.TYPES_NAMESPACE}::{cpp_naming.enum_name(node.identifier)}"
1044
                ),
1045
                None,
1046
            )
1047

1048
        return None, Error(
×
1049
            node.original_node,
1050
            f"We can not determine how to transpile the name {node.identifier!r} "
1051
            f"to C++. We could not find it neither in the local variables, "
1052
            f"nor in the global constants, nor in verification functions, "
1053
            f"nor as an enumeration. If you expect this name to be transpilable, "
1054
            f"please contact the developers.",
1055
        )
1056

1057

1058
@ensure(lambda result: (result[0] is not None) ^ (result[1] is not None))
3✔
1059
def _transpile_class_invariant(
3✔
1060
    invariant: intermediate.Invariant,
1061
    symbol_table: intermediate.SymbolTable,
1062
    environment: intermediate_type_inference.Environment,
1063
) -> Tuple[Optional[Stripped], Optional[Error]]:
1064
    """Translate the invariant from the meta-model into a C++ condition."""
1065
    # fmt: off
1066
    type_map, inference_error = (
3✔
1067
        intermediate_type_inference.infer_for_invariant(
1068
            invariant=invariant,
1069
            environment=environment
1070
        )
1071
    )
1072
    # fmt: on
1073

1074
    if inference_error is not None:
3✔
1075
        return None, inference_error
×
1076

1077
    assert type_map is not None
3✔
1078

1079
    optional_inferrer = cpp_optionaling.Inferrer(
3✔
1080
        environment=environment, type_map=type_map
1081
    )
1082

1083
    _ = optional_inferrer.transform(invariant.body)
3✔
1084

1085
    if len(optional_inferrer.errors) > 0:
3✔
1086
        return None, Error(
×
1087
            invariant.parsed.node,
1088
            "Failed to infer whether one or more nodes are ``common::optional`` "
1089
            "in the invariant",
1090
            optional_inferrer.errors,
1091
        )
1092

1093
    transpiler = _ClassInvariantTranspiler(
3✔
1094
        type_map=type_map,
1095
        is_optional_map=optional_inferrer.is_optional_map,
1096
        environment=environment,
1097
        symbol_table=symbol_table,
1098
    )
1099

1100
    expr, error = transpiler.transform(invariant.body)
3✔
1101

1102
    if error is not None:
3✔
1103
        return None, error
×
1104

1105
    assert expr is not None
3✔
1106
    return expr, None
3✔
1107

1108

1109
@require(lambda verificator_qualities: not verificator_qualities.is_noop)
3✔
1110
@ensure(lambda result: (result[0] is not None) ^ (result[1] is not None))
3✔
1111
def _generate_non_recursive_verificator_execute(
3✔
1112
    verificator_qualities: VerificatorQualities,
1113
    symbol_table: intermediate.SymbolTable,
1114
    environment: intermediate_type_inference.Environment,
1115
) -> Tuple[Optional[Stripped], Optional[List[Error]]]:
1116
    """Generate the impl. of the ``Execute()`` for a verificator of class ``cls``."""
1117
    flow = [
3✔
1118
        yielding_flow.command_from_text(
1119
            """\
1120
done_ = false;
1121
error_ = nullptr;
1122
index_ = -1;"""
1123
        )
1124
    ]  # type: List[yielding_flow.Node]
1125

1126
    for prop in verificator_qualities.constrained_primitive_properties:
3✔
1127
        if isinstance(
3✔
1128
            intermediate.beneath_optional(prop.type_annotation),
1129
            intermediate.ListTypeAnnotation,
1130
        ):
1131
            flow.append(
3✔
1132
                yielding_flow.command_from_text(
1133
                    """\
1134
// NOTE (mristin):
1135
// We reset the index in constrained primitives for easier debugging since this bears
1136
// negligible overhead.
1137
index_in_constrained_primitives_ = static_cast<std::size_t>(-1);"""
1138
                )
1139
            )
1140

1141
    errors = []  # type: List[Error]
3✔
1142
    for invariant in verificator_qualities.cls.invariants:
3✔
1143
        condition_expr, error = _transpile_class_invariant(
3✔
1144
            invariant=invariant, symbol_table=symbol_table, environment=environment
1145
        )
1146
        if error is not None:
3✔
1147
            errors.append(error)
×
1148
            continue
×
1149

1150
        assert condition_expr is not None
3✔
1151

1152
        # NOTE (mristin):
1153
        # We need to wrap the description in multiple literals as a single long
1154
        # string literal is often too much for the readability.
1155
        invariant_description_lines = wrap_text_into_lines(invariant.description)
3✔
1156

1157
        invariant_description_literals_joined = "\n".join(
3✔
1158
            cpp_common.wstring_literal(line) for line in invariant_description_lines
1159
        )
1160

1161
        flow.append(
3✔
1162
            yielding_flow.IfFalse(
1163
                condition_expr,
1164
                [
1165
                    yielding_flow.command_from_text(
1166
                        f"""\
1167
error_ = common::make_unique<Error>(
1168
{I}{indent_but_first_line(invariant_description_literals_joined, I)}
1169
);
1170
// No path is prepended as the error refers to the instance itself.
1171
++index_;"""
1172
                    ),
1173
                    yielding_flow.Yield(),
1174
                ],
1175
            )
1176
        )
1177

1178
    for prop in verificator_qualities.constrained_primitive_properties:
3✔
1179
        constrained_primitive = _find_constrained_primitive(prop.type_annotation)
3✔
1180
        assert constrained_primitive is not None
3✔
1181

1182
        type_anno = intermediate.beneath_optional(prop.type_annotation)
3✔
1183

1184
        of_constrained_primitive = cpp_naming.class_name(
3✔
1185
            Identifier(f"Of_{constrained_primitive.name}")
1186
        )
1187

1188
        property_enum = cpp_naming.enum_name(Identifier("Property"))
3✔
1189
        property_literal = cpp_naming.enum_literal_name(prop.name)
3✔
1190

1191
        getter_name = cpp_naming.getter_name(prop.name)
3✔
1192

1193
        getter_expr: Stripped
1194
        if isinstance(prop.type_annotation, intermediate.OptionalTypeAnnotation):
3✔
1195
            getter_expr = Stripped(f"*(instance_->{getter_name}())")
3✔
1196
        else:
1197
            getter_expr = Stripped(f"instance_->{getter_name}()")
3✔
1198

1199
        flow_for_prop: List[yielding_flow.Node]
1200

1201
        if isinstance(type_anno, intermediate.PrimitiveTypeAnnotation):
3✔
1202
            raise AssertionError(
×
1203
                f"Expected only a type annotation for a property containing "
1204
                f"a constrained primitive, but got: {prop.type_annotation}"
1205
            )
1206

1207
        elif isinstance(type_anno, intermediate.OurTypeAnnotation):
3✔
1208
            assert isinstance(type_anno.our_type, intermediate.ConstrainedPrimitive)
3✔
1209
            assert type_anno.our_type is constrained_primitive
3✔
1210

1211
            # noinspection PyListCreation
1212
            flow_for_prop = []
3✔
1213

1214
            # NOTE (mristin):
1215
            # We call ``append`` to avoid the double indention with ``extend([...])``.
1216

1217
            flow_for_prop.append(
3✔
1218
                yielding_flow.command_from_text(
1219
                    f"""\
1220
constrained_primitive_verificator_ = (
1221
{I}common::make_unique<
1222
{II}constrained_primitive_verificator::{of_constrained_primitive}
1223
{I}>(
1224
{II}{indent_but_first_line(getter_expr, II)}
1225
{I})
1226
);
1227
constrained_primitive_verificator_->Start();"""
1228
                )
1229
            )
1230
            flow_for_prop.append(
3✔
1231
                yielding_flow.For(
1232
                    "!constrained_primitive_verificator_->Done()",
1233
                    "constrained_primitive_verificator_->Next();",
1234
                    [
1235
                        yielding_flow.command_from_text(
1236
                            f"""\
1237
// We intentionally take over the ownership of the errors' data members,
1238
// as we know the implementation in all the detail, and want to avoid a costly
1239
// copy.
1240
error_ = common::make_unique<Error>(
1241
{I}std::move(
1242
{II}constrained_primitive_verificator_->GetMutable()
1243
{I})
1244
);
1245

1246
error_->path.segments.emplace_back(
1247
{I}common::make_unique<iteration::PropertySegment>(
1248
{II}iteration::{property_enum}::{property_literal}
1249
{I})
1250
);
1251

1252
++index_;"""
1253
                        ),
1254
                        yielding_flow.Yield(),
1255
                    ],
1256
                )
1257
            )
1258
            flow_for_prop.append(
3✔
1259
                yielding_flow.command_from_text(
1260
                    "constrained_primitive_verificator_ = nullptr;"
1261
                )
1262
            )
1263

1264
        elif isinstance(type_anno, intermediate.ListTypeAnnotation):
3✔
1265
            if not (
3✔
1266
                isinstance(type_anno.items, intermediate.OurTypeAnnotation)
1267
                and isinstance(
1268
                    type_anno.items.our_type, intermediate.ConstrainedPrimitive
1269
                )
1270
            ):
1271
                raise NotImplementedError(
×
1272
                    f"NOTE (mristin): We implemented only non-recursive verification of "
1273
                    f"lists of constrained primitives at the moment, "
1274
                    f"but we got: {prop.type_annotation}. "
1275
                    f"Please contact the developers if you need this feature."
1276
                )
1277

1278
            # noinspection PyListCreation
1279
            flow_for_prop = []
3✔
1280

1281
            # NOTE (mristin):
1282
            # We call ``append`` to avoid the double indention with ``extend([...])``.
1283

1284
            flow_for_prop.append(
3✔
1285
                yielding_flow.command_from_text(
1286
                    """\
1287
index_in_constrained_primitives_ = 0;"""
1288
                )
1289
            )
1290

1291
            at_expr = f"({getter_expr}).at(index_in_constrained_primitives_)"
3✔
1292

1293
            flow_for_prop.append(
3✔
1294
                yielding_flow.For(
1295
                    f"index_in_constrained_primitives_ < ({getter_expr}).size()",
1296
                    "++index_in_constrained_primitives_;",
1297
                    [
1298
                        yielding_flow.command_from_text(
1299
                            f"""\
1300
constrained_primitive_verificator_ = (
1301
{I}common::make_unique<
1302
{II}constrained_primitive_verificator::{of_constrained_primitive}
1303
{I}>(
1304
{II}{indent_but_first_line(at_expr, II)}
1305
{I})
1306
);
1307
constrained_primitive_verificator_->Start();"""
1308
                        ),
1309
                        yielding_flow.For(
1310
                            "!constrained_primitive_verificator_->Done()",
1311
                            "constrained_primitive_verificator_->Next();",
1312
                            [
1313
                                yielding_flow.command_from_text(
1314
                                    f"""\
1315
// We intentionally take over the ownership of the errors' data members,
1316
// as we know the implementation in all the detail, and want to avoid a costly
1317
// copy.
1318
error_ = common::make_unique<Error>(
1319
{I}std::move(
1320
{II}constrained_primitive_verificator_->GetMutable()
1321
{I})
1322
);
1323

1324
error_->path.segments.emplace_back(
1325
{I}common::make_unique<iteration::IndexSegment>(
1326
{II}index_in_constrained_primitives_
1327
{I})
1328
);
1329

1330
error_->path.segments.emplace_back(
1331
{I}common::make_unique<iteration::PropertySegment>(
1332
{II}iteration::{property_enum}::{property_literal}
1333
{I})
1334
);
1335

1336
++index_;"""
1337
                                ),
1338
                                yielding_flow.Yield(),
1339
                            ],
1340
                        ),
1341
                        yielding_flow.command_from_text(
1342
                            "constrained_primitive_verificator_ = nullptr;"
1343
                        ),
1344
                    ],
1345
                )
1346
            )
1347

1348
        else:
UNCOV
1349
            assert_never(type_anno)
×
1350

1351
        if isinstance(prop.type_annotation, intermediate.OptionalTypeAnnotation):
3✔
1352
            flow_for_prop = [
3✔
1353
                yielding_flow.IfTrue(
1354
                    condition=f"instance_->{getter_name}().has_value()",
1355
                    body=flow_for_prop,
1356
                )
1357
            ]
1358

1359
        flow.extend(flow_for_prop)
3✔
1360

1361
    flow.append(
3✔
1362
        yielding_flow.command_from_text(
1363
            """\
1364
done_ = true;
1365
error_ = nullptr;
1366
index_ = -1;"""
1367
        )
1368
    )
1369

1370
    if len(errors) > 0:
3✔
1371
        return None, errors
×
1372

1373
    code = cpp_yielding.generate_execute_body(
3✔
1374
        flow=flow, state_member=Identifier("state_")
1375
    )
1376

1377
    of_cls = cpp_naming.class_name(Identifier(f"of_{verificator_qualities.cls.name}"))
3✔
1378

1379
    return (
3✔
1380
        Stripped(
1381
            f"""\
1382
void {of_cls}::Execute() {{
1383
{I}{indent_but_first_line(code, I)}
1384
}}"""
1385
        ),
1386
        None,
1387
    )
1388

1389

1390
@require(lambda verificator_qualities: not verificator_qualities.is_noop)
3✔
1391
@ensure(lambda result: (result[0] is not None) ^ (result[1] is not None))
3✔
1392
def _generate_non_recursive_verificator_implementation(
3✔
1393
    verificator_qualities: VerificatorQualities,
1394
    symbol_table: intermediate.SymbolTable,
1395
    environment: intermediate_type_inference.Environment,
1396
) -> Tuple[Optional[List[Stripped]], Optional[Error]]:
1397
    """Generate the impl. of a non-recursive verificator for ``cls``."""
1398
    cls = verificator_qualities.cls
3✔
1399

1400
    of_cls = cpp_naming.class_name(Identifier(f"Of_{cls.name}"))
3✔
1401

1402
    interface_name = cpp_naming.interface_name(cls.name)
3✔
1403

1404
    copy_data_members_snippet = Stripped(
3✔
1405
        """\
1406
instance_ = other.instance_;
1407
done_ = other.done_;
1408
index_ = other.index_;
1409
error_ = common::make_unique<Error>(*other.error_);
1410
state_ = other.state_;"""
1411
    )
1412

1413
    move_data_members_snippet = Stripped(
3✔
1414
        """\
1415
instance_ = std::move(other.instance_);
1416
done_ = other.done_;
1417
index_ = other.index_;
1418
error_ = std::move(other.error_);
1419
state_ = other.state_;"""
1420
    )
1421

1422
    if len(verificator_qualities.constrained_primitive_properties) > 0:
3✔
1423
        copy_data_members_snippet = Stripped(
3✔
1424
            f"""\
1425
{copy_data_members_snippet}
1426
constrained_primitive_verificator_ = (
1427
{I}other.constrained_primitive_verificator_->Clone()
1428
);"""
1429
        )
1430

1431
        move_data_members_snippet = Stripped(
3✔
1432
            f"""\
1433
{move_data_members_snippet}
1434
constrained_primitive_verificator_ = std::move(
1435
{I}other.constrained_primitive_verificator_
1436
);"""
1437
        )
1438

1439
    blocks = [
3✔
1440
        Stripped(
1441
            f"""\
1442
{of_cls}::{of_cls}(
1443
{I}const std::shared_ptr<types::IClass>& instance
1444
) :
1445
{I}// NOTE (mristin)
1446
{I}// We cast here despite the cost of increasing the use count of the shared pointer.
1447
{I}// Otherwise, if we didn't cast, we would not be able to have a uniform interface
1448
{I}// for the verification functions based on the shared pointer.
1449
{I}instance_(
1450
{II}std::dynamic_pointer_cast<
1451
{III}types::{interface_name}
1452
{II}>(
1453
{III}instance
1454
{II})
1455
{I}) {{
1456
{I}// Intentionally empty.
1457
}}"""
1458
        ),
1459
        Stripped(
1460
            f"""\
1461
{of_cls}::{of_cls}(
1462
{I}const {of_cls}& other
1463
) {{
1464
{I}{indent_but_first_line(copy_data_members_snippet, I)}
1465
}}"""
1466
        ),
1467
        Stripped(
1468
            f"""\
1469
{of_cls}::{of_cls}(
1470
{I}{of_cls}&& other
1471
) {{
1472
{I}{indent_but_first_line(move_data_members_snippet, I)}
1473
}}"""
1474
        ),
1475
        Stripped(
1476
            f"""\
1477
{of_cls}& {of_cls}::operator=(
1478
{I}const {of_cls}& other
1479
) {{
1480
{I}return *this = {of_cls}(other);
1481
}}"""
1482
        ),
1483
        Stripped(
1484
            f"""\
1485
{of_cls}& {of_cls}::operator=(
1486
{I}{of_cls}&& other
1487
) {{
1488
{I}if (this != &other) {{
1489
{II}{indent_but_first_line(move_data_members_snippet, II)}
1490
{I}}}
1491
{I}return *this;
1492
}}"""
1493
        ),
1494
        Stripped(
1495
            f"""\
1496
void {of_cls}::Start() {{
1497
{I}state_ = 0;
1498
{I}Execute();
1499
}}"""
1500
        ),
1501
        Stripped(
1502
            f"""\
1503
void {of_cls}::Next() {{
1504
{I}#ifdef DEBUG
1505
{I}if (Done()) {{
1506
{II}throw std::logic_error(
1507
{III}"You want to move a verificator {of_cls}, "
1508
{III}"but the verificator was done."
1509
{II});
1510
{I}}}
1511
{I}#endif
1512

1513
{I}Execute();
1514
}}"""
1515
        ),
1516
        Stripped(
1517
            f"""\
1518
bool {of_cls}::Done() const {{
1519
{I}return done_;
1520
}}"""
1521
        ),
1522
        Stripped(
1523
            f"""\
1524
const Error& {of_cls}::Get() const {{
1525
{I}#ifdef DEBUG
1526
{I}if (Done()) {{
1527
{II}throw std::logic_error(
1528
{III}"You want to get from a verificator {of_cls}, "
1529
{III}"but the verificator was done."
1530
{II});
1531
{I}}}
1532
{I}#endif
1533

1534
{I}return *error_;
1535
}}"""
1536
        ),
1537
        Stripped(
1538
            f"""\
1539
Error& {of_cls}::GetMutable() {{
1540
{I}#ifdef DEBUG
1541
{I}if (Done()) {{
1542
{II}throw std::logic_error(
1543
{III}"You want to get mutable from a verificator {of_cls}, "
1544
{III}"but the verificator was done."
1545
{II});
1546
{I}}}
1547
{I}#endif
1548

1549
{I}return *error_;
1550
}}"""
1551
        ),
1552
        Stripped(
1553
            f"""\
1554
long {of_cls}::Index() const {{
1555
{I}#ifdef DEBUG
1556
{I}if (Done() && index_ != -1) {{
1557
{II}throw std::logic_error(
1558
{III}common::Concat(
1559
{IIII}"Expected index to be -1 "
1560
{IIII}"from a done verificator {of_cls}, "
1561
{IIII}"but got: ",
1562
{IIII}std::to_string(index_)
1563
{III})
1564
{II});
1565
{I}}}
1566
{I}#endif
1567

1568
{I}return index_;
1569
}}"""
1570
        ),
1571
        Stripped(
1572
            f"""\
1573
std::unique_ptr<impl::IVerificator> {of_cls}::Clone() const {{
1574
{I}return common::make_unique<
1575
{II}{of_cls}
1576
{I}>(*this);
1577
}}"""
1578
        ),
1579
    ]  # type: List[Stripped]
1580

1581
    execute_block, execute_errors = _generate_non_recursive_verificator_execute(
3✔
1582
        verificator_qualities=verificator_qualities,
1583
        symbol_table=symbol_table,
1584
        environment=environment,
1585
    )
1586
    if execute_errors is not None:
3✔
1587
        return None, Error(
×
1588
            cls.parsed.node,
1589
            f"Failed to generate Execute() method as {of_cls!r}",
1590
            execute_errors,
1591
        )
1592

1593
    assert execute_block is not None
3✔
1594
    blocks.append(execute_block)
3✔
1595

1596
    return blocks, None
3✔
1597

1598

1599
@ensure(lambda result: (result[0] is not None) ^ (result[1] is not None))
3✔
1600
def _generate_non_recursive_verificator(
3✔
1601
    cls: intermediate.ConcreteClass,
1602
    symbol_table: intermediate.SymbolTable,
1603
    base_environment: intermediate_type_inference.Environment,
1604
) -> Tuple[Optional[List[Stripped]], Optional[Error]]:
1605
    """Generate the non-recursive verificator for the ``cls``."""
1606
    environment = intermediate_type_inference.MutableEnvironment(
3✔
1607
        parent=base_environment
1608
    )
1609

1610
    assert environment.find(Identifier("self")) is None
3✔
1611
    environment.set(
3✔
1612
        identifier=Identifier("self"),
1613
        type_annotation=intermediate_type_inference.OurTypeAnnotation(our_type=cls),
1614
    )
1615

1616
    verificator_qualities = VerificatorQualities(cls=cls)
3✔
1617

1618
    if verificator_qualities.is_noop:
3✔
1619
        return (
3✔
1620
            _generate_empty_non_recursive_verificator(
1621
                verificator_qualities=verificator_qualities
1622
            ),
1623
            None,
1624
        )
1625

1626
    of_cls = cpp_naming.class_name(Identifier(f"Of_{cls.name}"))
3✔
1627

1628
    interface_name = cpp_naming.interface_name(cls.name)
3✔
1629

1630
    private_data_members = [
3✔
1631
        Stripped(
1632
            f"""\
1633
std::shared_ptr<types::{interface_name}> instance_;
1634
bool done_;
1635
long index_;
1636
std::unique_ptr<Error> error_;
1637
std::uint32_t state_;"""
1638
        )
1639
    ]  # type: List[Stripped]
1640

1641
    if len(verificator_qualities.constrained_primitive_properties) > 0:
3✔
1642
        private_data_members.append(
3✔
1643
            Stripped(
1644
                "std::unique_ptr<impl::IVerificator> "
1645
                "constrained_primitive_verificator_;"
1646
            )
1647
        )
1648

1649
        for prop in verificator_qualities.constrained_primitive_properties:
3✔
1650
            if isinstance(
3✔
1651
                intermediate.beneath_optional(prop.type_annotation),
1652
                intermediate.ListTypeAnnotation,
1653
            ):
1654
                # NOTE (mristin):
1655
                # Since there is a list of constrained primitives, we will have to
1656
                # iterate over it.
1657
                private_data_members.append(
3✔
1658
                    Stripped("std::size_t index_in_constrained_primitives_;")
1659
                )
1660

1661
    private_data_members_joined = "\n".join(private_data_members)
3✔
1662

1663
    blocks = [
3✔
1664
        Stripped(
1665
            f"""\
1666
class {of_cls} : public impl::IVerificator {{
1667
 public:
1668
{I}{of_cls}(
1669
{II}const std::shared_ptr<types::IClass>& instance
1670
{I});
1671

1672
{I}{of_cls}(
1673
{II}const {of_cls}& other
1674
{I});
1675
{I}{of_cls}(
1676
{II}{of_cls}&& other
1677
{I});
1678
{I}{of_cls}& operator=(
1679
{II}const {of_cls}& other
1680
{I});
1681
{I}{of_cls}& operator=(
1682
{II}{of_cls}&& other
1683
{I});
1684

1685
{I}void Start() override;
1686
{I}void Next() override;
1687
{I}bool Done() const override;
1688
{I}const Error& Get() const override;
1689
{I}Error& GetMutable() override;
1690
{I}long Index() const override;
1691

1692
{I}std::unique_ptr<impl::IVerificator> Clone() const override;
1693

1694
{I}~{of_cls}() override = default;
1695

1696
 private:
1697
{I}{indent_but_first_line(private_data_members_joined, I)}
1698

1699
{I}void Execute();
1700
}};  // class {of_cls}"""
1701
        )
1702
    ]  # type: List[Stripped]
1703

1704
    impl_blocks, error = _generate_non_recursive_verificator_implementation(
3✔
1705
        verificator_qualities=verificator_qualities,
1706
        symbol_table=symbol_table,
1707
        environment=environment,
1708
    )
1709
    if error is not None:
3✔
1710
        return None, error
×
1711

1712
    assert impl_blocks is not None
3✔
1713
    blocks.extend(impl_blocks)
3✔
1714

1715
    return blocks, None
3✔
1716

1717

1718
def _generate_new_non_recursive_verificator_implementation(
3✔
1719
    symbol_table: intermediate.SymbolTable,
1720
) -> Stripped:
1721
    """Generate the factory of non-recursive verificators based on the model type."""
1722
    case_blocks = []  # type: List[Stripped]
3✔
1723
    for cls in symbol_table.concrete_classes:
3✔
1724
        enum_name = cpp_naming.enum_name(Identifier("Model_type"))
3✔
1725
        literal_name = cpp_naming.enum_literal_name(cls.name)
3✔
1726
        verificator_of_cls = cpp_naming.class_name(Identifier(f"Of_{cls.name}"))
3✔
1727

1728
        case_blocks.append(
3✔
1729
            Stripped(
1730
                f"""\
1731
case types::{enum_name}::{literal_name}:
1732
{I}return common::make_unique<
1733
{II}non_recursive_verificator::{verificator_of_cls}
1734
{I}>(
1735
{II}instance
1736
{I});"""
1737
            )
1738
        )
1739

1740
    case_blocks.append(
3✔
1741
        Stripped(
1742
            f"""\
1743
default:
1744
{I}throw std::logic_error(
1745
{II}common::Concat(
1746
{III}"Unexpected model type: ",
1747
{III}std::to_string(
1748
{IIII}static_cast<std::uint32_t>(instance->model_type())
1749
{III})
1750
{II})
1751
{I});"""
1752
        )
1753
    )
1754

1755
    case_blocks_joined = "\n".join(case_blocks)
3✔
1756

1757
    switch_stmt = Stripped(
3✔
1758
        f"""\
1759
switch (instance->model_type()) {{
1760
{I}{indent_but_first_line(case_blocks_joined, I)}
1761
}}"""
1762
    )
1763

1764
    new_non_recursive_verificator = cpp_naming.function_name(
3✔
1765
        Identifier("new_non_recursive_verificator")
1766
    )
1767

1768
    return Stripped(
3✔
1769
        f"""\
1770
std::unique_ptr<impl::IVerificator> {new_non_recursive_verificator}(
1771
{I}const std::shared_ptr<types::IClass>& instance
1772
) {{
1773
{I}{indent_but_first_line(switch_stmt, I)}
1774
}}"""
1775
    )
1776

1777

1778
def _generate_recursive_verificator_execute() -> Stripped:
3✔
1779
    """Generate the impl. of the ``Execute()`` method for recursive verificator."""
1780
    flow = [
3✔
1781
        yielding_flow.command_from_text(
1782
            """\
1783
error_ = nullptr;
1784
index_ = -1;
1785
done_ = false;
1786

1787
verificator_ = NewNonRecursiveVerificator(*instance_);
1788
verificator_->Start();"""
1789
        ),
1790
        yielding_flow.For(
1791
            "!verificator_->Done()",
1792
            "verificator_->Next();",
1793
            [
1794
                yielding_flow.command_from_text(
1795
                    f"""\
1796
// We intentionally take over the ownership of the errors' data members,
1797
// as we know the implementation in all the detail, and want to avoid a costly
1798
// copy.
1799
error_ = common::make_unique<Error>(
1800
{I}std::move(
1801
{II}verificator_->GetMutable()
1802
{I})
1803
);
1804
// No path is prepended as the error refers to the instance itself.
1805
++index_;"""
1806
                ),
1807
                yielding_flow.Yield(),
1808
            ],
1809
        ),
1810
        yielding_flow.command_from_text(
1811
            f"""\
1812
verificator_ = nullptr;
1813

1814
{{
1815
{I}// NOTE (mristin):
1816
{I}// We will not need descent, so we introduce it in the scope.
1817
{I}iteration::Descent descent(
1818
{II}*instance_
1819
{I});
1820
{I}iterator_ = std::move(descent.begin());
1821

1822
{I}// NOTE (mristin):
1823
{I}// descent.end() is a constant reference, so we make an explicit
1824
{I}// copy here.
1825
{I}iterator_end_ = descent.end();
1826
}}"""
1827
        ),
1828
        yielding_flow.For(
1829
            "*iterator_ != *iterator_end_",
1830
            "++(*iterator_);",
1831
            [
1832
                yielding_flow.command_from_text(
1833
                    f"""\
1834
verificator_ = NewNonRecursiveVerificator(
1835
{I}*(*iterator_)
1836
);
1837
verificator_->Start();"""
1838
                ),
1839
                yielding_flow.For(
1840
                    "!verificator_->Done()",
1841
                    "verificator_->Next();",
1842
                    [
1843
                        yielding_flow.command_from_text(
1844
                            f"""\
1845
// We intentionally take over the ownership of the errors' data members,
1846
// as we know the implementation in all the detail, and want to avoid a costly
1847
// copy.
1848
error_ = common::make_unique<Error>(
1849
{I}std::move(
1850
{II}verificator_->GetMutable()
1851
{I})
1852
);
1853

1854
error_->path = std::move(
1855
{I}iteration::MaterializePath(
1856
{II}*iterator_
1857
{I})
1858
);
1859

1860
++index_;"""
1861
                        ),
1862
                        yielding_flow.Yield(),
1863
                    ],
1864
                ),
1865
                yielding_flow.command_from_text("verificator_ = nullptr;"),
1866
            ],
1867
        ),
1868
        yielding_flow.command_from_text(
1869
            """\
1870
iterator_.reset();
1871
iterator_end_.reset();
1872
done_ = true;
1873
index_ = -1;"""
1874
        ),
1875
    ]  # type: Sequence[yielding_flow.Node]
1876

1877
    code = cpp_yielding.generate_execute_body(
3✔
1878
        flow=flow, state_member=Identifier("state_")
1879
    )
1880

1881
    return Stripped(
3✔
1882
        f"""\
1883
void RecursiveVerificator::Execute() {{
1884
{I}{indent_but_first_line(code, I)}
1885
}}"""
1886
    )
1887

1888

1889
def _generate_recursive_verificator() -> List[Stripped]:
3✔
1890
    """Generate the impl. and definition of the recursive verificator."""
1891
    blocks = [
3✔
1892
        Stripped(
1893
            f"""\
1894
class RecursiveVerificator : public impl::IVerificator {{
1895
 public:
1896
{I}RecursiveVerificator(
1897
{II}const std::shared_ptr<types::IClass>& instance
1898
{I});
1899

1900
{I}RecursiveVerificator(const RecursiveVerificator& other);
1901
{I}RecursiveVerificator(RecursiveVerificator&& other);
1902
{I}RecursiveVerificator& operator=(const RecursiveVerificator& other);
1903
{I}RecursiveVerificator& operator=(RecursiveVerificator&& other);
1904

1905
{I}void Start() override;
1906
{I}void Next() override;
1907
{I}bool Done() const override;
1908
{I}const Error& Get() const override;
1909
{I}Error& GetMutable() override;
1910
{I}long Index() const override;
1911

1912
{I}std::unique_ptr<impl::IVerificator> Clone() const override;
1913

1914
{I}~RecursiveVerificator() override = default;
1915

1916
 private:
1917
{I}// NOTE(mristin):
1918
{I}// We use a pointer to a shared pointer here so that we can implement
1919
{I}// copy-assignment and move-assignment. Otherwise, if we used a constant
1920
{I}// reference here, the assignments could not be implemented as C++ does not
1921
{I}// allow re-binding of constant references.
1922
{I}const std::shared_ptr<types::IClass>* instance_;
1923

1924
{I}std::uint32_t state_;
1925
{I}std::unique_ptr<impl::IVerificator> verificator_;
1926
{I}bool done_;
1927
{I}long index_;
1928
{I}std::unique_ptr<Error> error_;
1929
{I}common::optional<iteration::Iterator> iterator_;
1930
{I}common::optional<iteration::Iterator> iterator_end_;
1931

1932
{I}void Execute();
1933
}};  // class RecursiveVerificator"""
1934
        ),
1935
        Stripped(
1936
            f"""\
1937
RecursiveVerificator::RecursiveVerificator(
1938
{I}const std::shared_ptr<types::IClass>& instance
1939
) : instance_(&instance) {{
1940
{I}// Intentionally empty.
1941
}}"""
1942
        ),
1943
        Stripped(
1944
            f"""\
1945
RecursiveVerificator::RecursiveVerificator(const RecursiveVerificator& other) {{
1946
{I}instance_ = other.instance_;
1947
{I}state_ = other.state_;
1948
{I}verificator_ = other.verificator_->Clone();
1949
{I}done_ = other.done_;
1950
{I}index_ = other.index_;
1951
{I}error_ = common::make_unique<Error>(*(other.error_));
1952
{I}iterator_ = other.iterator_;
1953
{I}iterator_end_ = other.iterator_end_;
1954
}}"""
1955
        ),
1956
        Stripped(
1957
            f"""\
1958
RecursiveVerificator::RecursiveVerificator(RecursiveVerificator&& other) {{
1959
{I}instance_ = other.instance_;
1960
{I}state_ = other.state_;
1961
{I}verificator_ = std::move(other.verificator_);
1962
{I}done_ = other.done_;
1963
{I}index_ = other.index_;
1964
{I}error_ = std::move(other.error_);
1965
{I}iterator_ = std::move(other.iterator_);
1966
{I}iterator_end_ = std::move(other.iterator_end_);
1967
}}"""
1968
        ),
1969
        Stripped(
1970
            f"""\
1971
RecursiveVerificator& RecursiveVerificator::operator=(
1972
{I}const RecursiveVerificator& other
1973
) {{
1974
{I}return *this = RecursiveVerificator(other);
1975
}}"""
1976
        ),
1977
        Stripped(
1978
            f"""\
1979
RecursiveVerificator& RecursiveVerificator::operator=(RecursiveVerificator&& other) {{
1980
{I}if (this != &other) {{
1981
{II}instance_ = other.instance_;
1982
{II}state_ = other.state_;
1983
{II}verificator_ = std::move(other.verificator_);
1984
{II}done_ = other.done_;
1985
{II}index_ = other.index_;
1986
{II}error_ = std::move(other.error_);
1987
{II}iterator_ = std::move(other.iterator_);
1988
{II}iterator_end_ = std::move(other.iterator_end_);
1989
{I}}}
1990

1991
{I}return *this;
1992
}}"""
1993
        ),
1994
        Stripped(
1995
            f"""\
1996
void RecursiveVerificator::Start() {{
1997
{I}state_ = 0;
1998
{I}Execute();
1999
}}"""
2000
        ),
2001
        Stripped(
2002
            f"""\
2003
void RecursiveVerificator::Next() {{
2004
{I}#ifdef DEBUG
2005
{I}if (Done()) {{
2006
{II}throw std::logic_error(
2007
{III}"You want to get from a RecursiveVerificator, "
2008
{III}"but the verificator was done."
2009
{II});
2010
{I}}}
2011
{I}#endif
2012

2013
{I}Execute();
2014
}}"""
2015
        ),
2016
        Stripped(
2017
            f"""\
2018
bool RecursiveVerificator::Done() const {{
2019
{I}return done_;
2020
}}"""
2021
        ),
2022
        Stripped(
2023
            f"""\
2024
const Error& RecursiveVerificator::Get() const {{
2025
{I}#ifdef DEBUG
2026
{I}if (Done()) {{
2027
{II}throw std::logic_error(
2028
{III}"You want to get from a RecursiveVerificator, "
2029
{III}"but the verificator is done."
2030
{II});
2031
{I}}}
2032
{I}#endif
2033

2034
{I}return *error_;
2035
}}"""
2036
        ),
2037
        Stripped(
2038
            f"""\
2039
Error& RecursiveVerificator::GetMutable() {{
2040
{I}#ifdef DEBUG
2041
{I}if (Done()) {{
2042
{II}throw std::logic_error(
2043
{III}"You want to get mutable from a RecursiveVerificator, "
2044
{III}"but the verificator is done."
2045
{II});
2046
{I}}}
2047
{I}#endif
2048

2049
{I}return *error_;
2050
}}"""
2051
        ),
2052
        Stripped(
2053
            f"""\
2054
long RecursiveVerificator::Index() const {{
2055
{I}#ifdef DEBUG
2056
{I}if (Done() && index_ != -1) {{
2057
{II}throw std::logic_error(
2058
{III}common::Concat(
2059
{IIII}"Expected index to be -1 "
2060
{IIII}"from a done RecursiveVerificator, "
2061
{IIII}"but got: ",
2062
{IIII}std::to_string(index_)
2063
{III})
2064
{II});
2065
{I}}}
2066
{I}#endif
2067

2068
{I}return index_;
2069
}}"""
2070
        ),
2071
        Stripped(
2072
            f"""\
2073
std::unique_ptr<impl::IVerificator> RecursiveVerificator::Clone() const {{
2074
{I}return common::make_unique<RecursiveVerificator>(*this);
2075
}}"""
2076
        ),
2077
    ]  # type: List[Stripped]
2078

2079
    execute_block = _generate_recursive_verificator_execute()
3✔
2080
    blocks.append(execute_block)
3✔
2081

2082
    return blocks
3✔
2083

2084

2085
def _generate_pattern_verification_implementation(
3✔
2086
    verification: intermediate.PatternVerification,
2087
) -> Stripped:
2088
    """Generate the implementation of the given pattern verification function."""
2089
    assert len(verification.arguments) == 1
3✔
2090
    arg = verification.arguments[0]
3✔
2091

2092
    arg_type = cpp_common.generate_type_with_const_ref_if_applicable(
3✔
2093
        type_annotation=arg.type_annotation, types_namespace=cpp_common.TYPES_NAMESPACE
2094
    )
2095
    arg_name = cpp_naming.argument_name(arg.name)
3✔
2096

2097
    verification_name = cpp_naming.function_name(verification.name)
3✔
2098

2099
    program_name = cpp_naming.constant_name(Identifier(f"{verification.name}_program"))
3✔
2100

2101
    return Stripped(
3✔
2102
        f"""\
2103
bool {verification_name}(
2104
{I}{indent_but_first_line(arg_type, I)} {arg_name}
2105
) {{
2106
{I}return revm::Match(
2107
{II}pattern::{program_name},
2108
{II}{arg_name}
2109
{I});
2110
}}"""
2111
    )
2112

2113

2114
class _TranspilableVerificationTranspiler(cpp_transpilation.Transpiler):
3✔
2115
    """Transpile the body of a :class:`TranspilableVerification`."""
2116

2117
    # fmt: off
2118
    @require(
3✔
2119
        lambda environment, verification:
2120
        all(
2121
            environment.find(arg.name) is not None
2122
            for arg in verification.arguments
2123
        ),
2124
        "All arguments defined in the environment"
2125
    )
2126
    # fmt: on
2127
    def __init__(
3✔
2128
        self,
2129
        type_map: Mapping[
2130
            parse_tree.Node, intermediate_type_inference.TypeAnnotationUnion
2131
        ],
2132
        is_optional_map: Mapping[parse_tree.Node, bool],
2133
        environment: intermediate_type_inference.Environment,
2134
        symbol_table: intermediate.SymbolTable,
2135
        verification: intermediate.TranspilableVerification,
2136
    ) -> None:
2137
        """Initialize with the given values."""
2138
        cpp_transpilation.Transpiler.__init__(
3✔
2139
            self,
2140
            type_map=type_map,
2141
            is_optional_map=is_optional_map,
2142
            environment=environment,
2143
            types_namespace=cpp_common.TYPES_NAMESPACE,
2144
        )
2145

2146
        self._symbol_table = symbol_table
3✔
2147

2148
        self._argument_name_set = frozenset(arg.name for arg in verification.arguments)
3✔
2149

2150
    def transform_name(
3✔
2151
        self, node: parse_tree.Name
2152
    ) -> Tuple[Optional[Stripped], Optional[Error]]:
2153
        if node.identifier in self._variable_name_set:
3✔
2154
            return Stripped(cpp_naming.variable_name(node.identifier)), None
×
2155

2156
        if node.identifier in self._argument_name_set:
3✔
2157
            return Stripped(cpp_naming.argument_name(node.identifier)), None
3✔
2158

2159
        if node.identifier in self._symbol_table.constants_by_name:
3✔
2160
            constant = cpp_naming.constant_name(node.identifier)
3✔
2161
            return Stripped(f"{cpp_common.CONSTANTS_NAMESPACE}::{constant}"), None
3✔
2162

2163
        if node.identifier in self._symbol_table.verification_functions_by_name:
3✔
2164
            return Stripped(cpp_naming.function_name(node.identifier)), None
×
2165

2166
        our_type = self._symbol_table.find_our_type(name=node.identifier)
3✔
2167
        if isinstance(our_type, intermediate.Enumeration):
3✔
2168
            return (
3✔
2169
                Stripped(
2170
                    f"{cpp_common.TYPES_NAMESPACE}::{cpp_naming.enum_name(node.identifier)}"
2171
                ),
2172
                None,
2173
            )
2174

2175
        return None, Error(
×
2176
            node.original_node,
2177
            f"We can not determine how to transpile the name {node.identifier!r} "
2178
            f"to C++. We could not find it neither in the constants, nor in "
2179
            f"verification functions, nor as an enumeration. "
2180
            f"If you expect this name to be transpilable, please contact "
2181
            f"the developers.",
2182
        )
2183

2184

2185
@ensure(lambda result: (result[0] is not None) ^ (result[1] is not None))
3✔
2186
def _generate_implementation_of_transpilable_verification(
3✔
2187
    verification: intermediate.TranspilableVerification,
2188
    symbol_table: intermediate.SymbolTable,
2189
    base_environment: intermediate_type_inference.Environment,
2190
) -> Tuple[Optional[Stripped], Optional[Error]]:
2191
    """Transpile the verification to a function implementation."""
2192
    # fmt: off
2193
    type_inference, inference_error = (
3✔
2194
        intermediate_type_inference.infer_for_verification(
2195
            verification=verification,
2196
            base_environment=base_environment
2197
        )
2198
    )
2199
    # fmt: on
2200

2201
    if inference_error is not None:
3✔
2202
        return None, inference_error
×
2203

2204
    assert type_inference is not None
3✔
2205

2206
    optional_inferrer = cpp_optionaling.Inferrer(
3✔
2207
        environment=type_inference.environment_with_args,
2208
        type_map=type_inference.type_map,
2209
    )
2210
    for node in verification.parsed.body:
3✔
2211
        _ = optional_inferrer.transform(node)
3✔
2212

2213
    if len(optional_inferrer.errors) > 0:
3✔
2214
        return None, Error(
×
2215
            verification.parsed.node,
2216
            f"Failed to infer whether one or more nodes are ``common::optional`` "
2217
            f"in the verification function {verification.name!r}",
2218
            optional_inferrer.errors,
2219
        )
2220

2221
    transpiler = _TranspilableVerificationTranspiler(
3✔
2222
        type_map=type_inference.type_map,
2223
        is_optional_map=optional_inferrer.is_optional_map,
2224
        environment=type_inference.environment_with_args,
2225
        symbol_table=symbol_table,
2226
        verification=verification,
2227
    )
2228

2229
    body = []  # type: List[Stripped]
3✔
2230
    for node in verification.parsed.body:
3✔
2231
        stmt, error = transpiler.transform(node)
3✔
2232
        if error is not None:
3✔
2233
            return None, Error(
×
2234
                verification.parsed.node,
2235
                f"Failed to transpile the verification function {verification.name!r}",
2236
                [error],
2237
            )
2238

2239
        assert stmt is not None
3✔
2240
        body.append(stmt)
3✔
2241

2242
    arg_types_names = [
3✔
2243
        (
2244
            cpp_common.generate_type_with_const_ref_if_applicable(
2245
                type_annotation=arg.type_annotation,
2246
                types_namespace=cpp_common.TYPES_NAMESPACE,
2247
            ),
2248
            cpp_naming.argument_name(arg.name),
2249
        )
2250
        for arg in verification.arguments
2251
    ]
2252

2253
    function_name = cpp_naming.function_name(verification.name)
3✔
2254
    arg_definitions_joined = ",\n".join(
3✔
2255
        f"{arg_type} {arg_name}" for arg_type, arg_name in arg_types_names
2256
    )
2257

2258
    body_joined = "\n".join(body)
3✔
2259

2260
    return (
3✔
2261
        Stripped(
2262
            f"""\
2263
bool {function_name}(
2264
{I}{indent_but_first_line(arg_definitions_joined, I)}
2265
) {{
2266
{I}{indent_but_first_line(body_joined, I)}
2267
}}"""
2268
        ),
2269
        None,
2270
    )
2271

2272

2273
@require(lambda constrained_primitive: len(constrained_primitive.invariants) == 0)
3✔
2274
def _generate_empty_constrained_primitive_verificator(
3✔
2275
    constrained_primitive: intermediate.ConstrainedPrimitive,
2276
) -> List[Stripped]:
2277
    """
2278
    Generate a constrained primitive verificator which is always done.
2279

2280
    Though the implementation is a duplicate in logic of ``AlwaysDoneVerificator``,
2281
    the assertion error messages are different, so we generate a separate class.
2282
    """
2283
    of_constrained_primitive = cpp_naming.class_name(
3✔
2284
        Identifier(f"Of_{constrained_primitive.name}")
2285
    )
2286

2287
    value_type = cpp_common.generate_primitive_type_with_const_ref_if_applicable(
3✔
2288
        primitive_type=constrained_primitive.constrainee
2289
    )
2290

2291
    return [
3✔
2292
        Stripped(
2293
            f"""\
2294
class {of_constrained_primitive} : public impl::IVerificator {{
2295
 public:
2296
{I}{of_constrained_primitive}(
2297
{II}{value_type} value
2298
{I});
2299

2300
{I}void Start() override;
2301
{I}void Next() override;
2302
{I}bool Done() const override;
2303
{I}const Error& Get() const override;
2304
{I}Error& GetMutable() override;
2305
{I}long Index() const override;
2306

2307
{I}std::unique_ptr<impl::IVerificator> Clone() const override;
2308

2309
{I}virtual ~{of_constrained_primitive}() = default;
2310
}};  // class {of_constrained_primitive}"""
2311
        ),
2312
        Stripped(
2313
            f"""\
2314
{of_constrained_primitive}::{of_constrained_primitive}(
2315
{I}{value_type}
2316
) {{
2317
{I}// Intentionally empty.
2318
}}"""
2319
        ),
2320
        Stripped(
2321
            f"""\
2322
void {of_constrained_primitive}::Start() {{
2323
{I}// Intentionally empty.
2324
}}"""
2325
        ),
2326
        Stripped(
2327
            f"""\
2328
void {of_constrained_primitive}::Next() {{
2329
{I}throw std::logic_error(
2330
{II}"You want to move "
2331
{II}"a verificator {of_constrained_primitive}, "
2332
{II}"but the verificator is always done as "
2333
{II}"there are no invariants defined for this constrained primitive."
2334
{I});
2335
}}"""
2336
        ),
2337
        Stripped(
2338
            f"""\
2339
bool {of_constrained_primitive}::Done() const {{
2340
{I}return true;
2341
}}"""
2342
        ),
2343
        Stripped(
2344
            f"""\
2345
const Error& {of_constrained_primitive}::Get() const {{
2346
{I}throw std::logic_error(
2347
{II}"You want to get from "
2348
{II}"a verificator {of_constrained_primitive}, "
2349
{II}"but the verificator is always done as "
2350
{II}"there are no invariants defined for this constrained primitive."
2351
{I});
2352
}}"""
2353
        ),
2354
        Stripped(
2355
            f"""\
2356
Error& {of_constrained_primitive}::GetMutable() {{
2357
{I}throw std::logic_error(
2358
{II}"You want to get mutable from "
2359
{II}"a verificator {of_constrained_primitive}, "
2360
{II}"but the verificator is always done as "
2361
{II}"there are no invariants defined for this constrained primitive."
2362
{I});
2363
}}"""
2364
        ),
2365
        Stripped(
2366
            f"""\
2367
long {of_constrained_primitive}::Index() const {{
2368
{I}return -1;
2369
}}"""
2370
        ),
2371
        Stripped(
2372
            f"""\
2373
std::unique_ptr<impl::IVerificator> {of_constrained_primitive}::Clone() const {{
2374
{I}return common::make_unique<
2375
{II}{of_constrained_primitive}
2376
{I}>(*this);
2377
}}"""
2378
        ),
2379
    ]
2380

2381

2382
class _ConstrainedPrimitiveInvariantTranspiler(cpp_transpilation.Transpiler):
3✔
2383
    """Transpile invariants of the constrained primitives."""
2384

2385
    def __init__(
3✔
2386
        self,
2387
        type_map: Mapping[
2388
            parse_tree.Node, intermediate_type_inference.TypeAnnotationUnion
2389
        ],
2390
        is_optional_map: Mapping[parse_tree.Node, bool],
2391
        environment: intermediate_type_inference.Environment,
2392
        symbol_table: intermediate.SymbolTable,
2393
        constrained_primitive: intermediate.ConstrainedPrimitive,
2394
    ) -> None:
2395
        """Initialize with the given values."""
2396
        cpp_transpilation.Transpiler.__init__(
3✔
2397
            self,
2398
            type_map=type_map,
2399
            is_optional_map=is_optional_map,
2400
            environment=environment,
2401
            types_namespace=cpp_common.TYPES_NAMESPACE,
2402
        )
2403

2404
        self._symbol_table = symbol_table
3✔
2405
        self._constrained_primitive = constrained_primitive
3✔
2406

2407
    def transform_name(
3✔
2408
        self, node: parse_tree.Name
2409
    ) -> Tuple[Optional[Stripped], Optional[Error]]:
2410
        if node.identifier in self._variable_name_set:
3✔
2411
            return Stripped(cpp_naming.variable_name(node.identifier)), None
×
2412

2413
        if node.identifier == "self":
3✔
2414
            # The ``value_`` refers to the value under verification.
2415
            if _constrained_primitive_verificator_value_is_pointer(
3✔
2416
                primitive_type=self._constrained_primitive.constrainee
2417
            ):
2418
                return Stripped("(*value_)"), None
3✔
2419
            else:
2420
                return Stripped("value_"), None
3✔
2421

2422
        if node.identifier in self._symbol_table.constants_by_name:
3✔
2423
            constant = cpp_naming.constant_name(node.identifier)
×
2424
            return Stripped(f"{cpp_common.CONSTANTS_NAMESPACE}::{constant}"), None
×
2425

2426
        if node.identifier in self._symbol_table.verification_functions_by_name:
3✔
2427
            return Stripped(cpp_naming.function_name(node.identifier)), None
3✔
2428

2429
        our_type = self._symbol_table.find_our_type(name=node.identifier)
×
2430
        if isinstance(our_type, intermediate.Enumeration):
×
2431
            return (
×
2432
                Stripped(
2433
                    f"{cpp_common.TYPES_NAMESPACE}::{cpp_naming.enum_name(node.identifier)}"
2434
                ),
2435
                None,
2436
            )
2437

2438
        return None, Error(
×
2439
            node.original_node,
2440
            f"We can not determine how to transpile the name {node.identifier!r} "
2441
            f"to C++. We could not find it neither in the local variables, "
2442
            f"nor in the global constants, nor in verification functions, "
2443
            f"nor as an enumeration. If you expect this name to be transpilable, "
2444
            f"please contact the developers.",
2445
        )
2446

2447

2448
# fmt: off
2449
@require(
3✔
2450
    lambda invariant, constrained_primitive:
2451
    id(invariant) in constrained_primitive.invariant_id_set
2452
)
2453
@ensure(lambda result: (result[0] is not None) ^ (result[1] is not None))
3✔
2454
# fmt: on
2455
def _transpile_constrained_primitive_invariant(
3✔
2456
    invariant: intermediate.Invariant,
2457
    symbol_table: intermediate.SymbolTable,
2458
    environment: intermediate_type_inference.Environment,
2459
    constrained_primitive: intermediate.ConstrainedPrimitive,
2460
) -> Tuple[Optional[Stripped], Optional[Error]]:
2461
    """Translate the invariant from the meta-model into a C++ condition."""
2462
    # fmt: off
2463
    type_map, inference_error = (
3✔
2464
        intermediate_type_inference.infer_for_invariant(
2465
            invariant=invariant,
2466
            environment=environment
2467
        )
2468
    )
2469
    # fmt: on
2470

2471
    if inference_error is not None:
3✔
2472
        return None, inference_error
×
2473

2474
    assert type_map is not None
3✔
2475

2476
    optional_inferrer = cpp_optionaling.Inferrer(
3✔
2477
        environment=environment, type_map=type_map
2478
    )
2479

2480
    _ = optional_inferrer.transform(invariant.body)
3✔
2481

2482
    if len(optional_inferrer.errors) > 0:
3✔
2483
        return None, Error(
×
2484
            invariant.parsed.node,
2485
            "Failed to infer whether one or more nodes are ``common::optional`` "
2486
            "in the invariant",
2487
            optional_inferrer.errors,
2488
        )
2489

2490
    transpiler = _ConstrainedPrimitiveInvariantTranspiler(
3✔
2491
        type_map=type_map,
2492
        is_optional_map=optional_inferrer.is_optional_map,
2493
        environment=environment,
2494
        symbol_table=symbol_table,
2495
        constrained_primitive=constrained_primitive,
2496
    )
2497

2498
    expr, error = transpiler.transform(invariant.body)
3✔
2499

2500
    if error is not None:
3✔
2501
        return None, error
×
2502

2503
    assert expr is not None
3✔
2504
    return expr, None
3✔
2505

2506

2507
@require(lambda constrained_primitive: len(constrained_primitive.invariants) > 0)
3✔
2508
def _generate_constrained_primitive_verificator_execute(
3✔
2509
    constrained_primitive: intermediate.ConstrainedPrimitive,
2510
    symbol_table: intermediate.SymbolTable,
2511
    environment: intermediate_type_inference.Environment,
2512
) -> Tuple[Optional[Stripped], Optional[List[Error]]]:
2513
    """Generate the ``Execute()`` in the constrained primitive verificator."""
2514
    flow = [
3✔
2515
        yielding_flow.command_from_text(
2516
            """\
2517
done_ = false;
2518
error_ = nullptr;
2519
index_ = -1;"""
2520
        )
2521
    ]  # type: List[yielding_flow.Node]
2522

2523
    errors = []  # type: List[Error]
3✔
2524
    for invariant in constrained_primitive.invariants:
3✔
2525
        condition_expr, error = _transpile_constrained_primitive_invariant(
3✔
2526
            invariant=invariant,
2527
            symbol_table=symbol_table,
2528
            environment=environment,
2529
            constrained_primitive=constrained_primitive,
2530
        )
2531
        if error is not None:
3✔
2532
            errors.append(error)
×
2533
            continue
×
2534

2535
        assert condition_expr is not None
3✔
2536

2537
        # NOTE (mristin):
2538
        # We need to wrap the description in multiple literals as a single long
2539
        # string literal is often too much for the readability.
2540
        invariant_description_lines = wrap_text_into_lines(invariant.description)
3✔
2541

2542
        invariant_description_literals_joined = "\n".join(
3✔
2543
            cpp_common.wstring_literal(line) for line in invariant_description_lines
2544
        )
2545

2546
        flow.append(
3✔
2547
            yielding_flow.IfFalse(
2548
                condition_expr,
2549
                [
2550
                    yielding_flow.command_from_text(
2551
                        f"""\
2552
error_ = common::make_unique<Error>(
2553
{I}{indent_but_first_line(invariant_description_literals_joined, I)}
2554
);
2555
// No path is prepended as the error refers to the value itself.
2556
++index_;"""
2557
                    ),
2558
                    yielding_flow.Yield(),
2559
                ],
2560
            )
2561
        )
2562

2563
    flow.append(
3✔
2564
        yielding_flow.command_from_text(
2565
            """\
2566
done_ = true;
2567
error_ = nullptr;
2568
index_ = -1;"""
2569
        )
2570
    )
2571

2572
    if len(errors) > 0:
3✔
2573
        return None, errors
×
2574

2575
    code = cpp_yielding.generate_execute_body(
3✔
2576
        flow=flow, state_member=Identifier("state_")
2577
    )
2578

2579
    of_constrained_primitive = cpp_naming.class_name(
3✔
2580
        Identifier(f"of_{constrained_primitive.name}")
2581
    )
2582

2583
    return (
3✔
2584
        Stripped(
2585
            f"""\
2586
void {of_constrained_primitive}::Execute() {{
2587
{I}{indent_but_first_line(code, I)}
2588
}}"""
2589
        ),
2590
        None,
2591
    )
2592

2593

2594
@ensure(lambda result: (result[0] is not None) ^ (result[1] is not None))
3✔
2595
def _generate_constrained_primitive_verificator(
3✔
2596
    constrained_primitive: intermediate.ConstrainedPrimitive,
2597
    symbol_table: intermediate.SymbolTable,
2598
    environment: intermediate_type_inference.Environment,
2599
) -> Tuple[Optional[List[Stripped]], Optional[Error]]:
2600
    """Generate the def. and impl. of a verificator for a constrained primitive."""
2601
    if len(constrained_primitive.invariants) == 0:
3✔
2602
        return (
3✔
2603
            _generate_empty_constrained_primitive_verificator(
2604
                constrained_primitive=constrained_primitive
2605
            ),
2606
            None,
2607
        )
2608

2609
    of_constrained_primitive = cpp_naming.class_name(
3✔
2610
        Identifier(f"Of_{constrained_primitive.name}")
2611
    )
2612

2613
    input_value_type = cpp_common.generate_primitive_type_with_const_ref_if_applicable(
3✔
2614
        primitive_type=constrained_primitive.constrainee
2615
    )
2616

2617
    value_type = cpp_common.generate_primitive_type(constrained_primitive.constrainee)
3✔
2618
    if _constrained_primitive_verificator_value_is_pointer(
3✔
2619
        constrained_primitive.constrainee
2620
    ):
2621
        data_value_type = f"const {value_type}*"
3✔
2622

2623
        constructor_init = "value_(&value)"
3✔
2624

2625
    else:
2626
        data_value_type = value_type
3✔
2627

2628
        constructor_init = "value_(value)"
3✔
2629

2630
    move_snippet = Stripped(
3✔
2631
        """\
2632
value_ = other.value_;
2633
index_ = other.index_;
2634
error_ = std::move(other.error_);
2635
done_ = other.done_;
2636
state_ = other.state_;"""
2637
    )
2638

2639
    blocks = [
3✔
2640
        Stripped(
2641
            f"""\
2642
class {of_constrained_primitive} : public impl::IVerificator {{
2643
 public:
2644
{I}{of_constrained_primitive}(
2645
{II}{input_value_type} value
2646
{I});
2647

2648
{I}{of_constrained_primitive}(
2649
{II}const {of_constrained_primitive}& other
2650
{I});
2651
{I}{of_constrained_primitive}(
2652
{II}{of_constrained_primitive}&& other
2653
{I});
2654
{I}{of_constrained_primitive}& operator=(
2655
{II}const {of_constrained_primitive}& other
2656
{I});
2657
{I}{of_constrained_primitive}& operator=(
2658
{II}{of_constrained_primitive}&& other
2659
{I});
2660

2661
{I}void Start() override;
2662
{I}void Next() override;
2663
{I}bool Done() const override;
2664
{I}const Error& Get() const override;
2665
{I}Error& GetMutable() override;
2666
{I}long Index() const override;
2667

2668
{I}std::unique_ptr<impl::IVerificator> Clone() const override;
2669

2670
{I}~{of_constrained_primitive}() override = default;
2671

2672
 private:
2673
{I}{data_value_type} value_;
2674
{I}long index_;
2675
{I}std::unique_ptr<Error> error_;
2676
{I}bool done_;
2677
{I}std::uint32_t state_;
2678

2679
{I}void Execute();
2680
}};  // class {of_constrained_primitive}"""
2681
        ),
2682
        Stripped(
2683
            f"""\
2684
{of_constrained_primitive}::{of_constrained_primitive}(
2685
{I}{input_value_type} value
2686
) : {constructor_init} {{
2687
{I}// Intentionally empty.
2688
}}"""
2689
        ),
2690
        Stripped(
2691
            f"""\
2692
{of_constrained_primitive}::{of_constrained_primitive}(
2693
{I}const {of_constrained_primitive}& other
2694
) {{
2695
{I}value_ = other.value_;
2696
{I}index_ = other.index_;
2697
{I}error_ = common::make_unique<Error>(*other.error_);
2698
{I}done_ = other.done_;
2699
{I}state_ = other.state_;
2700
}}"""
2701
        ),
2702
        Stripped(
2703
            f"""\
2704
{of_constrained_primitive}::{of_constrained_primitive}(
2705
{I}{of_constrained_primitive}&& other
2706
) {{
2707
{I}{indent_but_first_line(move_snippet, I)}
2708
}}"""
2709
        ),
2710
        Stripped(
2711
            f"""\
2712
{of_constrained_primitive}& {of_constrained_primitive}::operator=(
2713
{I}const {of_constrained_primitive}& other
2714
) {{
2715
{I}return *this = {of_constrained_primitive}(other);
2716
}}"""
2717
        ),
2718
        Stripped(
2719
            f"""\
2720
{of_constrained_primitive}& {of_constrained_primitive}::operator=(
2721
{I}{of_constrained_primitive}&& other
2722
) {{
2723
{I}if (this != &other) {{
2724
{II}{indent_but_first_line(move_snippet, II)}
2725
{I}}}
2726

2727
{I}return *this;
2728
}}"""
2729
        ),
2730
        Stripped(
2731
            f"""\
2732
void {of_constrained_primitive}::Start() {{
2733
{I}state_ = 0;
2734
{I}Execute();
2735
}}"""
2736
        ),
2737
        Stripped(
2738
            f"""\
2739
void {of_constrained_primitive}::Next() {{
2740
{I}#ifdef DEBUG
2741
{I}if (Done()) {{
2742
{II}throw std::logic_error(
2743
{III}"You want to move a verificator {of_constrained_primitive}, "
2744
{III}"but the verificator was done."
2745
{II});
2746
{I}}}
2747
{I}#endif
2748

2749
{I}Execute();
2750
}}"""
2751
        ),
2752
        Stripped(
2753
            f"""\
2754
bool {of_constrained_primitive}::Done() const {{
2755
{I}return done_;
2756
}}"""
2757
        ),
2758
        Stripped(
2759
            f"""\
2760
const Error& {of_constrained_primitive}::Get() const {{
2761
{I}#ifdef DEBUG
2762
{I}if (Done()) {{
2763
{II}throw std::logic_error(
2764
{III}"You want to get from a verificator {of_constrained_primitive}, "
2765
{III}"but the verificator was done."
2766
{II});
2767
{I}}}
2768
{I}#endif
2769

2770
{I}return *error_;
2771
}}"""
2772
        ),
2773
        Stripped(
2774
            f"""\
2775
Error& {of_constrained_primitive}::GetMutable() {{
2776
{I}#ifdef DEBUG
2777
{I}if (Done()) {{
2778
{II}throw std::logic_error(
2779
{III}"You want to get mutable from a verificator {of_constrained_primitive}, "
2780
{III}"but the verificator was done."
2781
{II});
2782
{I}}}
2783
{I}#endif
2784

2785
{I}return *error_;
2786
}}"""
2787
        ),
2788
        Stripped(
2789
            f"""\
2790
long {of_constrained_primitive}::Index() const {{
2791
{I}#ifdef DEBUG
2792
{I}if (Done() && index_ != -1) {{
2793
{II}throw std::logic_error(
2794
{III}common::Concat(
2795
{IIII}"Expected index to be -1 "
2796
{IIII}"from a done verificator {of_constrained_primitive}, "
2797
{IIII}"but got: ",
2798
{IIII}std::to_string(index_)
2799
{III})
2800
{II});
2801
{I}}}
2802
{I}#endif
2803

2804
{I}return index_;
2805
}}"""
2806
        ),
2807
        Stripped(
2808
            f"""\
2809
std::unique_ptr<impl::IVerificator> {of_constrained_primitive}::Clone() const {{
2810
{I}return common::make_unique<
2811
{II}{of_constrained_primitive}
2812
{I}>(*this);
2813
}}"""
2814
        ),
2815
    ]  # type: List[Stripped]
2816

2817
    execute_block, execute_errors = _generate_constrained_primitive_verificator_execute(
3✔
2818
        constrained_primitive=constrained_primitive,
2819
        symbol_table=symbol_table,
2820
        environment=environment,
2821
    )
2822
    if execute_errors is not None:
3✔
2823
        return None, Error(
×
2824
            constrained_primitive.parsed.node,
2825
            f"Failed to generate Execute() method for {of_constrained_primitive!r}",
2826
            execute_errors,
2827
        )
2828

2829
    assert execute_block is not None
3✔
2830
    blocks.append(execute_block)
3✔
2831

2832
    return blocks, None
3✔
2833

2834

2835
def _generate_implementation_of_verify_constrained_primitive(
3✔
2836
    constrained_primitive: intermediate.ConstrainedPrimitive,
2837
) -> Stripped:
2838
    """Generate the implementation of the function ``Verify{Constrained Primitive}``."""
2839
    verify_name = cpp_naming.function_name(
3✔
2840
        Identifier(f"verify_{constrained_primitive.name}")
2841
    )
2842

2843
    of_constrained_primitive = cpp_naming.class_name(
3✔
2844
        Identifier(f"Of_{constrained_primitive.name}")
2845
    )
2846

2847
    value_type = cpp_common.generate_primitive_type_with_const_ref_if_applicable(
3✔
2848
        primitive_type=constrained_primitive.constrainee
2849
    )
2850

2851
    return Stripped(
3✔
2852
        f"""\
2853
std::unique_ptr<IVerification> {verify_name}(
2854
{I}{value_type} that
2855
) {{
2856
{I}return common::make_unique<
2857
{II}constrained_primitive_verification::{of_constrained_primitive}
2858
{I}>(that);
2859
}}"""
2860
    )
2861

2862

2863
def _generate_constrained_primitive_verification(
3✔
2864
    constrained_primitive: intermediate.ConstrainedPrimitive,
2865
) -> List[Stripped]:
2866
    """Generate the verification class for the constrained primitive."""
2867
    of_constrained_primitive = cpp_naming.class_name(
3✔
2868
        Identifier(f"Of_{constrained_primitive.name}")
2869
    )
2870

2871
    value_type = cpp_common.generate_primitive_type_with_const_ref_if_applicable(
3✔
2872
        primitive_type=constrained_primitive.constrainee
2873
    )
2874

2875
    return [
3✔
2876
        Stripped(f"// region {of_constrained_primitive}"),
2877
        Stripped(
2878
            f"""\
2879
class {of_constrained_primitive} : public IVerification {{
2880
 public:
2881
{I}{of_constrained_primitive}(
2882
{II}{value_type} value
2883
{I});
2884

2885
{I}Iterator begin() const override;
2886
{I}const Iterator& end() const override;
2887

2888
{I}~{of_constrained_primitive}() override = default;
2889
 private:
2890
{I}{value_type} value_;
2891
}};  // class ConstrainedPrimitiveVerification"""
2892
        ),
2893
        Stripped(
2894
            f"""\
2895
{of_constrained_primitive}::{of_constrained_primitive}(
2896
{I}{value_type} value
2897
) : value_(value) {{
2898
{I}// Intentionally empty.
2899
}}"""
2900
        ),
2901
        Stripped(
2902
            f"""\
2903
Iterator {of_constrained_primitive}::begin() const {{
2904
{I}std::unique_ptr<impl::IVerificator> verificator(
2905
{II}common::make_unique<
2906
{III}constrained_primitive_verificator::{of_constrained_primitive}
2907
{II}>(value_)
2908
{I});
2909

2910
{I}verificator->Start();
2911

2912
{I}// NOTE(mristin):
2913
{I}// We short-circuit here for efficiency, as we can immediately dispose
2914
{I}// of the verificator.
2915
{I}if (verificator->Done()) {{
2916
{II}return Iterator(common::make_unique<AlwaysDoneVerificator>());
2917
{I}}}
2918

2919
{I}return Iterator(std::move(verificator));
2920
}}"""
2921
        ),
2922
        Stripped(
2923
            f"""\
2924
const Iterator& {of_constrained_primitive}::end() const {{
2925
{I}static Iterator iterator(common::make_unique<AlwaysDoneVerificator>());
2926
{I}return iterator;
2927
}}"""
2928
        ),
2929
        Stripped(f"// endregion {of_constrained_primitive}"),
2930
    ]
2931

2932

2933
# fmt: off
2934
@ensure(lambda result: (result[0] is not None) ^ (result[1] is not None))
3✔
2935
@ensure(
3✔
2936
    lambda result:
2937
    not (result[0] is not None) or result[0].endswith('\n'),
2938
    "Trailing newline mandatory for valid end-of-files"
2939
)
2940
# fmt: on
2941
def generate_implementation(
3✔
2942
    symbol_table: intermediate.SymbolTable,
2943
    spec_impls: specific_implementations.SpecificImplementations,
2944
    library_namespace: Stripped,
2945
) -> Tuple[Optional[str], Optional[List[Error]]]:
2946
    """Generate implementation of verification logic."""
2947
    namespace = Stripped(f"{library_namespace}::{cpp_common.VERIFICATION_NAMESPACE}")
3✔
2948

2949
    include_prefix_path = cpp_common.generate_include_prefix_path(library_namespace)
3✔
2950

2951
    errors = []  # type: List[Error]
3✔
2952

2953
    base_environment = intermediate_type_inference.populate_base_environment(
3✔
2954
        symbol_table=symbol_table
2955
    )
2956

2957
    blocks = [
3✔
2958
        cpp_common.WARNING,
2959
        Stripped(
2960
            f"""\
2961
#include "{include_prefix_path}/common.hpp"
2962
#include "{include_prefix_path}/constants.hpp"
2963
#include "{include_prefix_path}/pattern.hpp"
2964
#include "{include_prefix_path}/revm.hpp"
2965
#include "{include_prefix_path}/verification.hpp"
2966

2967
#pragma warning(push, 0)
2968
#include <map>
2969
#include <set>
2970
#pragma warning(pop)"""
2971
        ),
2972
        cpp_common.generate_namespace_opening(namespace),
2973
        *_generate_error_implementation(),
2974
        *_generate_always_done_verificator(),
2975
    ]  # type: List[Stripped]
2976

2977
    if len(symbol_table.verification_functions) > 0:
3✔
2978
        blocks.append(Stripped("// region Verification functions"))
3✔
2979

2980
        for verification in symbol_table.verification_functions:
3✔
2981
            if isinstance(verification, intermediate.PatternVerification):
3✔
2982
                blocks.append(
3✔
2983
                    _generate_pattern_verification_implementation(
2984
                        verification=verification
2985
                    )
2986
                )
2987

2988
            elif isinstance(verification, intermediate.TranspilableVerification):
3✔
2989
                block, error = _generate_implementation_of_transpilable_verification(
3✔
2990
                    verification=verification,
2991
                    symbol_table=symbol_table,
2992
                    base_environment=base_environment,
2993
                )
2994

2995
                if error is not None:
3✔
2996
                    errors.append(error)
×
2997
                else:
2998
                    assert block is not None
3✔
2999
                    blocks.append(block)
3✔
3000

3001
            elif isinstance(
3✔
3002
                verification, intermediate.ImplementationSpecificVerification
3003
            ):
3004
                implementation_key = specific_implementations.ImplementationKey(
3✔
3005
                    f"verification/{verification.name}.cpp"
3006
                )
3007

3008
                block = spec_impls.get(implementation_key, None)
3✔
3009

3010
                if block is None:
3✔
3011
                    errors.append(
×
3012
                        Error(
3013
                            verification.parsed.node,
3014
                            f"The implementation is missing for "
3015
                            f"the implementation-specific verification "
3016
                            f"function: {implementation_key}",
3017
                        )
3018
                    )
3019
                else:
3020
                    # NOTE (mristin):
3021
                    # Some verification functions only live in the header and have
3022
                    # no code in the implementation file. For example, the verification
3023
                    # functions which are templated.
3024
                    if len(block.strip()) > 0:
3✔
3025
                        blocks.append(block)
3✔
3026
            else:
3027
                assert_never(verification)
×
3028

3029
        blocks.append(Stripped("// endregion Verification functions"))
3✔
3030

3031
    if len(symbol_table.constrained_primitives) > 0:
3✔
3032
        blocks.append(Stripped("// region Verification of constrained primitives"))
3✔
3033

3034
        blocks.append(Stripped("namespace constrained_primitive_verificator {"))
3✔
3035

3036
        for constrained_primitive in symbol_table.constrained_primitives:
3✔
3037
            invariant_environment = intermediate_type_inference.MutableEnvironment(
3✔
3038
                parent=base_environment
3039
            )
3040

3041
            assert invariant_environment.find(Identifier("self")) is None
3✔
3042
            invariant_environment.set(
3✔
3043
                identifier=Identifier("self"),
3044
                type_annotation=intermediate_type_inference.OurTypeAnnotation(
3045
                    our_type=constrained_primitive
3046
                ),
3047
            )
3048

3049
            verificator_blocks, error = _generate_constrained_primitive_verificator(
3✔
3050
                constrained_primitive=constrained_primitive,
3051
                symbol_table=symbol_table,
3052
                environment=invariant_environment,
3053
            )
3054

3055
            if error is not None:
3✔
3056
                errors.append(error)
×
3057
            else:
3058
                assert verificator_blocks is not None
3✔
3059
                blocks.extend(verificator_blocks)
3✔
3060

3061
        blocks.append(Stripped("}  // namespace constrained_primitive_verificator"))
3✔
3062

3063
        blocks.append(Stripped("namespace constrained_primitive_verification {"))
3✔
3064

3065
        for constrained_primitive in symbol_table.constrained_primitives:
3✔
3066
            blocks.extend(
3✔
3067
                _generate_constrained_primitive_verification(
3068
                    constrained_primitive=constrained_primitive
3069
                )
3070
            )
3071

3072
        blocks.append(Stripped("}  // namespace constrained_primitive_verification"))
3✔
3073

3074
        for constrained_primitive in symbol_table.constrained_primitives:
3✔
3075
            blocks.append(
3✔
3076
                _generate_implementation_of_verify_constrained_primitive(
3077
                    constrained_primitive=constrained_primitive
3078
                )
3079
            )
3080

3081
        blocks.append(Stripped("// endregion Verification of constrained primitives"))
3✔
3082

3083
    blocks.extend(
3✔
3084
        [
3085
            _generate_new_non_recursive_verificator_definition(),
3086
            Stripped("// region Non-recursive verificators"),
3087
            Stripped("namespace non_recursive_verificator {"),
3088
        ]
3089
    )
3090

3091
    for cls in symbol_table.concrete_classes:
3✔
3092
        nrv_blocks, nrv_error = _generate_non_recursive_verificator(
3✔
3093
            cls=cls, symbol_table=symbol_table, base_environment=base_environment
3094
        )
3095
        if nrv_error is not None:
3✔
3096
            errors.append(nrv_error)
×
3097
        else:
3098
            assert nrv_blocks is not None
3✔
3099
            blocks.extend(nrv_blocks)
3✔
3100

3101
    blocks.append(Stripped("}  // namespace non_recursive_verificator"))
3✔
3102

3103
    blocks.extend(
3✔
3104
        [
3105
            _generate_new_non_recursive_verificator_implementation(
3106
                symbol_table=symbol_table
3107
            ),
3108
            Stripped("// endregion Non-recursive verificators"),
3109
            Stripped("// region Recursive verificators"),
3110
        ]
3111
    )
3112

3113
    blocks.extend(
3✔
3114
        [
3115
            *_generate_recursive_verificator(),
3116
            Stripped("// endregion Recursive verificators"),
3117
            *_generate_non_recursive_verification(),
3118
            *_generate_recursive_verification(),
3119
            *_generate_iterator_implementation(),
3120
        ]
3121
    )
3122

3123
    if len(errors) > 0:
3✔
3124
        return None, errors
×
3125

3126
    blocks.extend(
3✔
3127
        [
3128
            cpp_common.generate_namespace_closing(namespace),
3129
            cpp_common.WARNING,
3130
        ]
3131
    )
3132

3133
    writer = io.StringIO()
3✔
3134
    for i, block in enumerate(blocks):
3✔
3135
        if i > 0:
3✔
3136
            writer.write("\n\n")
3✔
3137

3138
        writer.write(block)
3✔
3139

3140
    writer.write("\n")
3✔
3141

3142
    return writer.getvalue(), None
3✔
3143

3144

3145
assert generate_header.__doc__ is not None
3✔
3146
cpp_common.assert_module_docstring_and_generate_header_consistent(
3✔
3147
    module_doc=__doc__, generate_header_doc=generate_header.__doc__
3148
)
3149

3150
assert generate_implementation.__doc__ is not None
3✔
3151
cpp_common.assert_module_docstring_and_generate_implementation_consistent(
3✔
3152
    module_doc=__doc__, generate_implementation_doc=generate_implementation.__doc__
3153
)
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