Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix handling of calls to boxed values, fix #1313 fix #1296 #1426

Merged
merged 6 commits into from May 15, 2023
Merged

Conversation

fridis
Copy link
Member

@fridis fridis commented May 11, 2023

Boxed value handling

Boxed values created from a value instance by assigning it to a compatible ref type are now different to ref types as follows: A boxed type is seen as a value type that was wrapped together with a type identitifier into a heap object.

The main problem that was solved is the use of t.this.type within a value type t. If an instance of t was boxed and became and instance of type ref t, uses of t, e.g., as the outer type of types defined within t's inner features would become ref t resulting in incompatibilities as shown in #1313.

The solution taken is that a boxed instance of t is of type ref t, but contains a copy of the original value instance of type t. Any access that is made to the boxed value is then made on this copied value instance, so internally t.this.type is still t.

The most important change this implies is that on a dynamically bound access to a feature (e.g., a call r.f on a reference r), if the actual value of r is some boxed value type v, f will be called on this value type and not on the corresonding boxed type ref v (except for for precondtions and constructors described below).

This change required some (partially unexpected) preliminary work that is part of this commit:

  • dynamic binding is now permitted for features with type parameters.

    For this to be possible, the code for dynamic feature lookup (Clazz.lookup) had to be extended to not look up a given feature, but to lookup a feature combined with its type parameters. A new class FeatureAndActuals was added for this.

  • constructors require special handling since they create new values whose type is the type defined by the constructor with the outer type defined by the target of the call.

    A call t.c hence creates an instance of T.c where T is the type of t. If t is a ref type and the actual value is a boxed value type v that inherits from T, we cannot call c on that value instance since the outer type must be T. Consequently, constructors when called on a ref target that is a boxed value type will be called on the boxed value, their outer type is a ref type. The result type of a call to a constructor t1.c is hence different to the result of a call to t2.c if t1 is a value type v and t2 is a ref type and the actual instance is the corresponding boxed type ref v.

  • preconditions require special handling since preconditions do not use dynamic binding, but static binding.

    If the static type of the target t of a call t.f is a boxed type ref v and f has a precondition, it is not possible to call the precondition on the target type v since t might refer to an instance of type w that inherits from v.

    So instead, preconditions may be called on boxed types, in this case the precondition will be called on static type ref v, while f will be called on the value type v or a different actual type the value t refers to.

    Due to the potential difference in target types for calls to preconditions, the code for call target lookup had to be change to take care for this. A flag was added to FeatureAndActuals to handle preconditions.

  • DFA could be simplified and no longer needs to determine value instances from ref clazzes.

  • The front end no longer needs the distinction between dynamic and static calls, AbstractFeature.isDynamic() was removed.

One change to the base lib was needed: CTrie.not_found had to be moved out of CTrie since the type of not_foound when used within CTrie is CTrie.this.type.not_found, which results in different and incompatible types for different possible heirs of CTrie.

New errors were introduced:

  • outerTypeMayNotBeRefType: It is now forbidden to use ref t as the outer type in source code, e.g., x (ref t).v := y is not possible.

  • illegalCallResultType: A call a.f whose target type is a boxed type ref v is forbiddent if the result type of fdepends on the actual outer type a.this.type. The reason is that in case 'a' is a different type that inherits from 'a', the resulting type would be incomptible.

  • illegalOuterRefTypeINcall: Similarly, A call a.f whose result type depends on the actual outer type a.this.type is forbidden if a is a ref type. The reason is that in case 'a''s actual value is a (boxed value) type that inherits from 'a', the resulting type would be incomptible.

Tests

Added tests for calls on boxed values

Also added negative tests for

  • AstErrors.illegalCallResultType,
  • AstErrors.illegalOuterRefTypeInCall, and
  • AstErrors.outerTypeMayNotBeRefType

various fixes for new handling of boxed value types

Due to a bug in the interpreter with mutable data, test valWithDynOuter is skipped for the interpreter for now.

fridis and others added 4 commits May 11, 2023 18:36
Boxed values created from a value instance by assigning it to a compatible ref
type are now different to ref types as follows: A boxed type is seen as a value
type that was wrapped together with a type identitifier into a heap object.

The main problem that was solved is the use of `t.this.type` within a value type
`t`. If an instance of `t` was boxed and became and instance of type `ref t`,
uses of `t`, e.g., as the outer type of types defined within `t`'s inner
features would become `ref t` resulting in incompatibilities as shown in #1313.

The solution taken is that a boxed instance of `t` is of type `ref t`, but
contains a copy of the original value instance of type `t`.  Any access that is
made to the boxed value is then made on this copied value instance, so
internally `t.this.type` is still `t`.

The most important change this implies is that on a dynamically bound access to
a feature (e.g., a call `r.f` on a reference `r`), if the actual value of `r` is
some boxed value type `v`, `f` will be called on this value type and not on the
corresonding boxed type `ref v` (except for for precondtions and constructors
described below).

This change required some (partially unexpected) preliminary work that is part
of this commit:

- dynamic binding is now permitted for features with type parameters.

  For this to be possible, the code for dynamic feature lookup (Clazz.lookup)
  had to be extended to not look up a given feature, but to lookup a feature
  combined with its type parameters.  A new class FeatureAndActuals was added
  for this.

- constructors require special handling since they create new values whose type
  is the type defined by the constructor with the outer type defined by the
  target of the call.

  A call `t.c` hence creates an instance of `T.c` where `T` is the type of `t`.
  If `t` is a ref type and the actual value is a boxed value type `v` that
  inherits from `T`, we cannot call `c` on that value instance since the outer
  type must be `T`. Consequently, constructors when called on a `ref` target
  that is a boxed value type will be called on the boxed value, their outer type
  is a `ref` type. The result type of a call to a constructor `t1.c` is hence
  different to the result of a call to `t2.c` if `t1` is a value type `v` and
  `t2` is a ref type and the actual instance is the corresponding boxed type
  `ref v`.

- preconditions require special handling since preconditions do not use dynamic
  binding, but static binding.

  If the static type of the target `t` of a call `t.f` is a boxed type `ref v`
  and `f` has a precondition, it is not possible to call the precondition on the
  target type `v` since `t` might refer to an instance of type `w` that inherits
  from `v`.

  So instead, preconditions may be called on boxed types, in this case the
  precondition will be called on static type `ref v`, while `f` will be called
  on the value type `v` or a different actual type the value `t` refers to.

  Due to the potential difference in target types for calls to preconditions,
  the code for call target lookup had to be change to take care for this.  A
  flag was added to FeatureAndActuals to handle preconditions.

- DFA could be simplified and no longer needs to determine value instances from
  ref clazzes.

- The front end no longer needs the distinction between dynamic and static calls,
  AbstractFeature.isDynamic() was removed.

One change to the base lib was needed: `CTrie.not_found` had to be moved out of
`CTrie` since the type of `not_foound` when used within `CTrie` is
`CTrie.this.type.not_found`, which results in different and incompatible types
for different possible heirs of `CTrie`.

New errors were introduced:

- outerTypeMayNotBeRefType: It is now forbidden to use `ref t` as the outer type
  in source code, e.g., `x (ref t).v := y` is not possible.

- illegalCallResultType: A call `a.f` whose target type is a boxed type `ref v`
  is forbiddent if the result type of `f`depends on the actual outer type
  `a.this.type`. The reason is that in case 'a' is a different type that
  inherits from 'a', the resulting type would be incomptible.

- illegalOuterRefTypeINcall: Similarly, A call `a.f` whose result type depends
  on the actual outer type `a.this.type` is forbidden if `a` is a `ref`
  type. The reason is that in case 'a''s actual value is a (boxed value) type
  that inherits from 'a', the resulting type would be incomptible.
Also added negative tests for

 - AstErrors.illegalCallResultType,
 - AstErrors.illegalOuterRefTypeInCall, and
 - AstErrors.outerTypeMayNotBeRefType
Due to a bug in the interpreter with mutable data, valWithDynOuter is skipped
for the interpreter for now.
- Fixed typos in comments
- added NYI for performance in O(n²)
- removed dead code `(true || xyz) &&`, unused local var `c`
- using simple.mk instead of simple_inp.mk
@fridis fridis merged commit 5f553be into main May 15, 2023
6 checks passed
@fridis fridis deleted the fix_1313 branch May 15, 2023 08:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants