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
Conversation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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.
michaellilltokiwa
approved these changes
May 12, 2023
- 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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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 typet
. If an instance oft
was boxed and became and instance of typeref t
, uses oft
, e.g., as the outer type of types defined withint
's inner features would becomeref t
resulting in incompatibilities as shown in #1313.The solution taken is that a boxed instance of
t
is of typeref t
, but contains a copy of the original value instance of typet
. Any access that is made to the boxed value is then made on this copied value instance, so internallyt.this.type
is stillt
.The most important change this implies is that on a dynamically bound access to a feature (e.g., a call
r.f
on a referencer
), if the actual value ofr
is some boxed value typev
,f
will be called on this value type and not on the corresonding boxed typeref 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 ofT.c
whereT
is the type oft
. Ift
is a ref type and the actual value is a boxed value typev
that inherits fromT
, we cannot callc
on that value instance since the outer type must beT
. Consequently, constructors when called on aref
target that is a boxed value type will be called on the boxed value, their outer type is aref
type. The result type of a call to a constructort1.c
is hence different to the result of a call tot2.c
ift1
is a value typev
andt2
is a ref type and the actual instance is the corresponding boxed typeref 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 callt.f
is a boxed typeref v
andf
has a precondition, it is not possible to call the precondition on the target typev
sincet
might refer to an instance of typew
that inherits fromv
.So instead, preconditions may be called on boxed types, in this case the precondition will be called on static type
ref v
, whilef
will be called on the value typev
or a different actual type the valuet
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 ofCTrie
since the type ofnot_foound
when used withinCTrie
isCTrie.this.type.not_found
, which results in different and incompatible types for different possible heirs ofCTrie
.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 typeref v
is forbiddent if the result type off
depends on the actual outer typea.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 typea.this.type
is forbidden ifa
is aref
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
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.