Skip to content

Commit 8b3dd6d

Browse files
authored
Merge pull request github#3572 from ginsbach/typeunions
introduce type unions in the handbook
2 parents e7800d4 + c97055f commit 8b3dd6d

File tree

2 files changed

+48
-1
lines changed

2 files changed

+48
-1
lines changed

docs/language/ql-handbook/aliases.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ of ``OldVersion``, you could deprecate the name ``OldVersion`` as follows::
4242
That way both names resolve to the same module, but if you use the name ``OldVersion``,
4343
a deprecation warning is displayed.
4444

45+
.. _type-aliases:
46+
4547
Type aliases
4648
============
4749

docs/language/ql-handbook/types.rst

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ than one type.
1414

1515
The kinds of types in QL are :ref:`primitive types <primitive-types>`, :ref:`classes <classes>`,
1616
:ref:`character types <character-types>`, :ref:`class domain types <domain-types>`,
17-
:ref:`algebraic datatypes <algebraic-datatypes>`, and :ref:`database types <database-types>`.
17+
:ref:`algebraic datatypes <algebraic-datatypes>`, :ref:`type unions <type-unions>`,
18+
and :ref:`database types <database-types>`.
1819

1920
.. index:: boolean, float, int, string, date
2021
.. _primitive-types:
@@ -479,6 +480,50 @@ program, so it's helpful to extend a new type (namely ``TTaintType``)::
479480
class Tainted extends TaintType, TTaintedValue {
480481
}
481482
483+
.. _type-unions:
484+
485+
Type unions
486+
***********
487+
488+
Type unions are user-defined types that are declared with the keyword ``class``.
489+
The syntax resembles :ref:`type aliases <type-aliases>`, but with two or more type expressions on the right-hand side.
490+
491+
Type unions are used for creating restricted subsets of an existing :ref:`algebraic datatype <algebraic-datatypes>`, by explicitly
492+
selecting a subset of the branches of that datatype and binding them to a new type.
493+
Type unions of :ref:`database types <database-types>` are also supported.
494+
495+
You can use a type union to give a name to a subset of the branches from an algebraic datatype.
496+
In some cases, using the type union over the whole algebraic datatype can avoid spurious
497+
:ref:`recursion <recursion>` in predicates.
498+
For example, the following construction is legal::
499+
500+
newtype InitialValueSource =
501+
ExplicitInitialization(VarDecl v) { exists(v.getInitializer()) } or
502+
ParameterPassing(Call c, int pos) { exists(c.getParameter(pos)) } or
503+
UnknownInitialGarbage(VarDecl v) { not exists(DefiniteInitialization di | v = target(di)) }
504+
505+
class DefiniteInitialization = ParameterPassing or ExplicitInitialization;
506+
507+
VarDecl target(DefiniteInitialization di) {
508+
di = ExplicitInitialization(result) or
509+
exists(Call c, int pos | di = ParameterPassing(c, pos) and
510+
result = c.getCallee().getFormalArg(pos))
511+
}
512+
513+
However, a similar implementation that restricts ``InitialValueSource`` in a class extension is not valid.
514+
If we had implemented ``DefiniteInitialization`` as a class extension instead, it would trigger a type test for ``InitialValueSource``. This results in an illegal recursion ``DefiniteInitialization -> InitialValueSource -> UnknownInitialGarbage -> ¬DefiniteInitialization`` since ``UnknownInitialGarbage`` relies on ``DefiniteInitialization``::
515+
516+
// THIS WON'T WORK: The implicit type check for InitialValueSource involves an illegal recursion
517+
// DefiniteInitialization -> InitialValueSource -> UnknownInitialGarbage -> ¬DefiniteInitialization!
518+
class DefiniteInitialization extends InitialValueSource {
519+
DefiniteInitialization() {
520+
this instanceof ParameterPassing or this instanceof ExplicitInitialization
521+
}
522+
// ...
523+
}
524+
525+
Type unions are supported from release 2.2.0 of the CodeQL CLI.
526+
482527
.. _database-types:
483528

484529
Database types

0 commit comments

Comments
 (0)