Skip to content

Commit 131d63c

Browse files
authored
Merge pull request github#6592 from github/ginsbach/instanceofDocs
language reference entry for non-extending subtypes
2 parents edbacec + 55c6059 commit 131d63c

File tree

1 file changed

+61
-6
lines changed

1 file changed

+61
-6
lines changed

docs/codeql/ql-language-reference/types.rst

Lines changed: 61 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ To define a class, you write:
7878
#. The keyword ``class``.
7979
#. The name of the class. This is an `identifier <https://codeql.github.com/docs/ql-language-reference/ql-language-specification/#identifiers>`_
8080
starting with an uppercase letter.
81-
#. The types to extend.
81+
#. The supertypes that the class is derived from via `extends` and/or `instanceof`
8282
#. The :ref:`body of the class <class-bodies>`, enclosed in braces.
8383

8484
For example:
@@ -106,12 +106,14 @@ This defines a class ``OneTwoThree``, which contains the values ``1``, ``2``, an
106106
.. index:: extends
107107

108108
``OneTwoThree`` extends ``int``, that is, it is a subtype of ``int``. A class in QL must always
109-
extend at least one existing type. Those types are called the **base types** of the class. The
110-
values of a class are contained within the intersection of the base types (that is, they are in
111-
the :ref:`class domain type <domain-types>`). A class inherits all member predicates from its
112-
base types.
109+
have at least one supertype. Supertypes that are referenced with the `extends` keyword are called
110+
the **base types** of the class. The values of a class are contained within the intersection of
111+
the supertypes (that is, they are in the :ref:`class domain type <domain-types>`).
112+
A class inherits all member predicates from its base types.
113113

114114
A class can extend multiple types. For more information, see ":ref:`multiple-inheritance`."
115+
Classes can also specialise other types without extending the class interface via `instanceof`,
116+
see ":ref:`instanceof-extensions`.".
115117

116118
To be valid, a class:
117119
- Must not extend itself.
@@ -228,7 +230,7 @@ Concrete classes
228230

229231
The classes in the above examples are all **concrete** classes. They are defined by
230232
restricting the values in a larger type. The values in a concrete class are precisely those
231-
values in the intersection of the base types that also satisfy the
233+
values in the intersection of the supertypes that also satisfy the
232234
:ref:`characteristic predicate <characteristic-predicates>` of the class.
233235

234236
.. _abstract-classes:
@@ -380,6 +382,59 @@ from ``OneTwoThree`` and ``int``.
380382
must :ref:`override <overriding-member-predicates>` those definitions to avoid ambiguity.
381383
:ref:`Super expressions <super>` are often useful in this situation.
382384

385+
.. _instanceof-extensions:
386+
387+
Non-extending subtypes
388+
======================
389+
390+
Besides extending base types, classes can also declare `instanceof` relationships with other types.
391+
Declaring a class as `instanceof Foo` is roughly equivalent to saying `this instanceof Foo` in the characteristic predicate.
392+
The main differences are that you can call methods on Bar via `super` and you can get better optimisation.
393+
394+
.. code-block:: ql
395+
396+
class Foo extends int {
397+
Foo() { this in [1 .. 10] }
398+
399+
string foo_method() { result = "foo" }
400+
}
401+
402+
class Bar instanceof Foo {
403+
string toString() { result = super.foo_method() }
404+
}
405+
406+
In this example, the characteristic predicate from `Foo` also applies to `Bar`.
407+
However, `foo_method` is not exposed in `Bar`, so the query `select any(Bar b).foo_method()`
408+
results in a compile time error. Note from the example that it is still possible to access
409+
methods from instanceof supertypes from within the specialising class with the `super` keyword.
410+
411+
Crucially, the instanceof **supertypes** are not **base types**.
412+
This means that these supertypes do not participate in overriding, and any fields of such
413+
supertypes are not part of the new class.
414+
This has implications on method resolution when complex class hierarchies are involved.
415+
The following example demonstrates this.
416+
417+
.. code-block:: ql
418+
419+
class Interface extends int {
420+
Interface() { this in [1 .. 10] }
421+
string foo() { result = "" }
422+
}
423+
424+
class Foo extends int {
425+
Foo() { this in [1 .. 5] }
426+
string foo() { result = "foo" }
427+
}
428+
429+
class Bar extends Interface instanceof Foo {
430+
override string foo() { result = "bar" }
431+
}
432+
433+
Here, the method `Bar::foo` does not override `Foo::foo`.
434+
Instead, it overrides only `Interface::foo`.
435+
This means that `select any(Foo f).foo()` yields only `foo`.
436+
Had `Bar` been defined as `extends Foo`, then `select any(Foo b)` would yield `bar`.
437+
383438
.. _character-types:
384439
.. _domain-types:
385440

0 commit comments

Comments
 (0)