Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
tpl_constraints.H
Go to the documentation of this file.
1/*
2 Aleph_w
3
4 Data structures & Algorithms
5 version 2.0.0b
6 https://github.com/lrleon/Aleph-w
7
8 This file is part of Aleph-w library
9
10 Copyright (c) 2002-2026 Leandro Rabindranath Leon
11
12 Permission is hereby granted, free of charge, to any person obtaining a copy
13 of this software and associated documentation files (the "Software"), to deal
14 in the Software without restriction, including without limitation the rights
15 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16 copies of the Software, and to permit persons to whom the Software is
17 furnished to do so, subject to the following conditions:
18
19 The above copyright notice and this permission notice shall be included in all
20 copies or substantial portions of the Software.
21
22 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28 SOFTWARE.
29*/
30
31
52# ifndef TPL_CONSTRAINTS_H
53# define TPL_CONSTRAINTS_H
54
55# include <sstream>
56# include <string>
57
58# include <Compiler_Types.H>
59# include <ah-errors.H>
60# include <ah-source.H>
61# include <tpl_dynArray.H>
62
63namespace Aleph
64{
66 template <typename T>
68 {
69 T lhs{};
70 T rhs{};
72 std::string message;
73 };
74
82 template <typename T>
84 {
86
87 public:
90 {
91 return constraints.size();
92 }
93
96 {
97 return constraints.is_empty();
98 }
99
102 {
103 constraints.clear();
104 }
105
114 size_t add(const T & lhs,
115 const T & rhs,
116 const Source_Span & span = {},
117 const std::string & message = "")
118 {
120 c.lhs = lhs;
121 c.rhs = rhs;
122 c.span = span;
123 c.message = message;
124 constraints.append(std::move(c));
125 return constraints.size();
126 }
127
134 const Equality_Constraint<T> & constraint(const size_t i) const
135 {
137 << "Constraint_Set::constraint(): invalid index " << i;
138 return constraints.access(i);
139 }
140 };
141
144
155
157 inline const char *
159 {
160 switch (kind)
161 {
162 case Compiler_Unify_Result_Kind::Success: return "Success";
163 case Compiler_Unify_Result_Kind::Invalid_Type: return "Invalid_Type";
164 case Compiler_Unify_Result_Kind::Kind_Mismatch: return "Kind_Mismatch";
165 case Compiler_Unify_Result_Kind::Arity_Mismatch: return "Arity_Mismatch";
166 case Compiler_Unify_Result_Kind::Occurs_Check_Failed: return "Occurs_Check_Failed";
167 case Compiler_Unify_Result_Kind::Rigid_Variable: return "Rigid_Variable";
168 }
169
170 return "Unknown";
171 }
172
188
191 {
192 public:
199
200 private:
202
205 {
206 auto current = id;
207 size_t remaining = bindings.size() + 1;
208 while (const auto * replacement = lookup(current))
209 {
210 current = *replacement;
211 ah_logic_error_if(remaining == 0)
212 << "Compiler_Type_Substitution::resolve_alias(): cyclic alias chain";
213 --remaining;
214 }
215 return current;
216 }
217
218 public:
221 {
222 return bindings.size();
223 }
224
227 {
228 return bindings.is_empty();
229 }
230
233 {
234 bindings.clear();
235 }
236
243 const Binding & binding(const size_t i) const
244 {
246 << "Compiler_Type_Substitution::binding(): invalid index " << i;
247 return bindings.access(i);
248 }
249
255 Compiler_Type_Id * lookup(const Compiler_Type_Id variable) noexcept
256 {
257 for (size_t i = bindings.size(); i > 0; --i)
258 if (bindings.access(i - 1).variable == variable)
259 return &bindings.access(i - 1).replacement;
260 return nullptr;
261 }
262
268 const Compiler_Type_Id * lookup(const Compiler_Type_Id variable) const noexcept
269 {
270 for (size_t i = bindings.size(); i > 0; --i)
271 if (bindings.access(i - 1).variable == variable)
272 return &bindings.access(i - 1).replacement;
273 return nullptr;
274 }
275
281 void bind(const Compiler_Type_Id variable,
282 const Compiler_Type_Id replacement)
283 {
284 if (auto * existing = lookup(variable); existing != nullptr)
285 {
286 *existing = replacement;
287 return;
288 }
289
290 bindings.append({variable, replacement});
291 }
292
303 const Compiler_Type_Id id) const
304 {
305 if (id == 0)
306 return 0;
307
308 const auto resolved = resolve_alias(id);
309 const auto & ty = ctx.type(resolved);
310 switch (ty.kind)
311 {
314 return resolved;
315
317 {
319 bool changed = false;
320 for (size_t i = 0; i < ty.components.size(); ++i)
321 {
322 const auto before = ty.components.access(i);
323 const auto after = apply(ctx, before);
324 members.append(after);
325 if (after != before)
326 changed = true;
327 }
328 return changed ? ctx.make_tuple_type(members) : resolved;
329 }
330
332 {
334 bool changed = false;
335 for (size_t i = 0; i < ty.components.size(); ++i)
336 {
337 const auto before = ty.components.access(i);
338 const auto after = apply(ctx, before);
339 params.append(after);
340 if (after != before)
341 changed = true;
342 }
343 const auto new_result = apply(ctx, ty.result_type);
344 if (new_result != ty.result_type)
345 changed = true;
346 return changed ? ctx.make_function_type(params, new_result) : resolved;
347 }
348
351 return resolved;
352 }
353
354 return resolved;
355 }
356
362 std::string to_string(Compiler_Type_Context & ctx) const
363 {
364 std::ostringstream out;
365 out << "Substitution\n";
366 for (size_t i = 0; i < bindings.size(); ++i)
367 {
368 const auto & b = bindings.access(i);
369 out << " " << ctx.to_string(b.variable)
370 << " := " << ctx.to_string(apply(ctx, b.replacement))
371 << '\n';
372 }
373 return out.str();
374 }
375 };
376
379 {
383
386 {
387 return {};
388 }
389
392 const Compiler_Type_Id lhs,
393 const Compiler_Type_Id rhs,
394 const std::string & message,
395 const Source_Span & span = {}) noexcept
396 {
398 result.kind = kind;
399 result.lhs = lhs;
400 result.rhs = rhs;
401 result.span = span;
402 result.message = message;
403 return result;
404 }
405
407 resolve(const Compiler_Type_Id id) const
408 {
409 if (id == 0)
410 return 0;
411
412 auto current = id;
413 size_t remaining = subst.size() + 1;
414 while (const auto * replacement = subst.lookup(current))
415 {
416 current = *replacement;
417 ah_logic_error_if(remaining == 0)
418 << "Compiler_Type_Unifier::resolve(): cyclic substitution";
419 --remaining;
420 }
421 return current;
422 }
423
424 bool
427 {
428 const auto resolved = resolve(candidate);
429 if (resolved == variable)
430 return true;
431 if (resolved == 0)
432 return false;
433
434 const auto & ty = context->type(resolved);
435 switch (ty.kind)
436 {
438 return false;
439
442 return false;
443
445 return resolved == variable;
446
448 for (size_t i = 0; i < ty.components.size(); ++i)
449 if (occurs_in(variable, ty.components.access(i)))
450 return true;
451 return false;
452
454 for (size_t i = 0; i < ty.components.size(); ++i)
455 if (occurs_in(variable, ty.components.access(i)))
456 return true;
457 return occurs_in(variable, ty.result_type);
458 }
459
460 return false;
461 }
462
465 const Compiler_Type_Id replacement)
466 {
467 const auto resolved_replacement = resolve(replacement);
468 if (variable == resolved_replacement)
469 return success();
470
471 const auto & var_type = context->type(variable);
472 if (var_type.rigid)
474 variable,
476 "cannot bind rigid type variable '"
477 + context->to_string(variable) + "'");
478
479 if (occurs_in(variable, resolved_replacement))
481 variable,
483 "occurs check failed while binding '"
484 + context->to_string(variable) + "' to '"
486
488 return success();
489 }
490
491 public:
496
499 {
500 subst.clear();
501 last = {};
502 }
503
506 {
507 return last;
508 }
509
512 {
513 return subst;
514 }
515
522 {
523 return subst.apply(*context, id);
524 }
525
535 const Compiler_Type_Id rhs)
536 {
537 if (lhs == 0 or rhs == 0)
539 lhs,
540 rhs,
541 "cannot unify null type ids");
542
543 const auto left = resolve(lhs);
544 const auto right = resolve(rhs);
545 if (left == right)
546 return last = success();
547
548 const auto & lhs_type = context->type(left);
549 const auto & rhs_type = context->type(right);
550
552 return last = bind_variable(left, right);
553
555 return last = bind_variable(right, left);
556
557 if (lhs_type.kind != rhs_type.kind)
559 left,
560 right,
561 "cannot unify '" + context->to_string(left)
562 + "' with '" + context->to_string(right) + "'");
563
564 switch (lhs_type.kind)
565 {
567 if (lhs_type.builtin == rhs_type.builtin)
568 return last = success();
570 left,
571 right,
572 "built-in type mismatch between '"
573 + context->to_string(left) + "' and '"
574 + context->to_string(right) + "'");
575
577 if (lhs_type.components.size() != rhs_type.components.size())
579 left,
580 right,
581 "tuple arity mismatch between '"
582 + context->to_string(left) + "' and '"
583 + context->to_string(right) + "'");
584
585 for (size_t i = 0; i < lhs_type.components.size(); ++i)
586 if (auto result = unify(lhs_type.components.access(i),
587 rhs_type.components.access(i));
588 not result.ok())
589 return last = result;
590 return last = success();
591
593 if (lhs_type.components.size() != rhs_type.components.size())
595 left,
596 right,
597 "function arity mismatch between '"
598 + context->to_string(left) + "' and '"
599 + context->to_string(right) + "'");
600
601 for (size_t i = 0; i < lhs_type.components.size(); ++i)
602 if (auto result = unify(lhs_type.components.access(i),
603 rhs_type.components.access(i));
604 not result.ok())
605 return last = result;
606 return last = unify(lhs_type.result_type, rhs_type.result_type);
607
611 left,
612 right,
613 "nominal type mismatch between '"
614 + context->to_string(left) + "' and '"
615 + context->to_string(right) + "'");
616
618 break;
619 }
620
621 return last = success();
622 }
623
633 {
634 for (size_t i = 0; i < constraints.size(); ++i)
635 {
636 const auto & c = constraints.constraint(i);
637 auto result = unify(c.lhs, c.rhs);
638 if (not result.ok())
639 {
640 result.span = c.span;
641 if (result.message.empty())
642 result.message = c.message;
643 else if (not c.message.empty())
644 result.message += ": " + c.message;
645 last = result;
646 return last;
647 }
648 }
649 return last = success();
650 }
651
653 std::string dump_substitution()
654 {
655 return subst.to_string(*context);
656 }
657 };
658}
659
660# endif // TPL_CONSTRAINTS_H
Stable type graph for the compiler-support MVP.
Exception handling system with formatted messages for Aleph-w.
#define ah_out_of_range_error_unless(C)
Throws std::out_of_range if condition does NOT hold.
Definition ah-errors.H:600
#define ah_logic_error_if(C)
Throws std::logic_error if condition holds.
Definition ah-errors.H:330
Source file and span management utilities for compiler-style tooling.
size_t size_t int32_t * out
Definition ca-c-api.h:120
Context owning all compiler type nodes.
std::string to_string(const Compiler_Type_Id id) const
Renders one type to a deterministic human-readable string.
const Compiler_Type & type(const Compiler_Type_Id id) const
Returns type id.
Compiler_Type_Id make_tuple_type(const DynArray< Compiler_Type_Id > &members)
Creates a tuple type.
Compiler_Type_Id make_function_type(const DynArray< Compiler_Type_Id > &parameters, const Compiler_Type_Id result)
Creates a function type.
Mutable substitution from type variables to replacement types.
void bind(const Compiler_Type_Id variable, const Compiler_Type_Id replacement)
Binds or updates one type-variable replacement.
const Compiler_Type_Id * lookup(const Compiler_Type_Id variable) const noexcept
Looks up a bound replacement for variable.
Compiler_Type_Id * lookup(const Compiler_Type_Id variable) noexcept
Looks up a bound replacement for variable.
void clear() noexcept
Removes every binding.
Compiler_Type_Id resolve_alias(const Compiler_Type_Id id) const
Compiler_Type_Id apply(Compiler_Type_Context &ctx, const Compiler_Type_Id id) const
Applies the substitution to id.
size_t size() const noexcept
Returns the number of stored variable bindings.
std::string to_string(Compiler_Type_Context &ctx) const
Renders all bindings in insertion order.
const Binding & binding(const size_t i) const
Returns binding i.
bool empty() const noexcept
Returns whether the substitution is empty.
Structural unifier for compiler types.
std::string dump_substitution()
Dumps the current substitution in a deterministic format.
Compiler_Unify_Result unify(const Compiler_Type_Id lhs, const Compiler_Type_Id rhs)
Attempts to unify lhs and rhs.
bool occurs_in(const Compiler_Type_Id variable, const Compiler_Type_Id candidate)
Compiler_Type_Unifier(Compiler_Type_Context &ctx) noexcept
Constructs a unifier operating over ctx.
Compiler_Unify_Result fail(const Compiler_Unify_Result_Kind kind, const Compiler_Type_Id lhs, const Compiler_Type_Id rhs, const std::string &message, const Source_Span &span={}) noexcept
Compiler_Unify_Result solve(const Compiler_Type_Constraint_Set &constraints)
Solves a whole ordered set of type constraints.
Compiler_Unify_Result last
Compiler_Type_Substitution subst
Compiler_Unify_Result bind_variable(const Compiler_Type_Id variable, const Compiler_Type_Id replacement)
const Compiler_Unify_Result & last_result() const noexcept
Returns the last result produced by unify() or solve().
static Compiler_Unify_Result success() noexcept
Compiler_Type_Id apply(const Compiler_Type_Id id)
Applies the current substitution to id.
void clear() noexcept
Clears substitutions and the last stored result.
const Compiler_Type_Substitution & substitution() const noexcept
Returns the accumulated substitution.
Compiler_Type_Context * context
Compiler_Type_Id resolve(const Compiler_Type_Id id) const
Ordered collection of equality constraints.
size_t size() const noexcept
Returns the number of stored constraints.
bool empty() const noexcept
Returns whether the set has no constraints.
void clear() noexcept
Removes all stored constraints.
DynArray< Equality_Constraint< T > > constraints
size_t add(const T &lhs, const T &rhs, const Source_Span &span={}, const std::string &message="")
Appends an equality constraint.
const Equality_Constraint< T > & constraint(const size_t i) const
Returns one stored constraint.
T & access(const size_t i) const noexcept
Fast access without checking allocation and bound_min_clock checking.
T & append()
Allocate a new entry to the end of array.
size_t blossom_maximum_cardinality_matching(const GT &g, DynDlist< typename GT::Arc * > &matching, SA sa=SA())
Alias of compute_maximum_cardinality_general_matching().
Definition Blossom.H:466
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
void message(const char *file, int line, const char *format,...)
Print an informational message with file and line info.
Definition ahDefs.C:95
std::decay_t< typename HeadC::Item_Type > T
Definition ah-zip.H:105
Compiler_Unify_Result_Kind
Result category returned by type unification.
size_t Compiler_Type_Id
const char * compiler_unify_result_kind_name(const Compiler_Unify_Result_Kind kind) noexcept
Returns a stable debug name for one unification result kind.
One variable binding inside the substitution.
Compiler_Type_Id replacement
Replacing type id.
Compiler_Type_Id variable
Bound type-variable id.
Outcome of one type-unification attempt.
bool ok() const noexcept
Returns whether the result denotes a successful unification.
Compiler_Unify_Result_Kind kind
Compiler_Type_Id lhs
Left type involved in the result.
std::string message
Human-readable explanation.
Source_Span span
Constraint span, if solving a stored constraint.
Compiler_Type_Id rhs
Right type involved in the result.
Equality constraint between two values of type T.
Source_Span span
Optional source span attached by the caller.
std::string message
Optional caller-defined explanation.
Half-open byte range inside a source file.
Definition ah-source.H:100
Lazy and scalable dynamic array implementation.