mirror of
https://github.com/erg-lang/erg.git
synced 2025-09-28 12:14:43 +00:00
51 lines
3 KiB
Markdown
51 lines
3 KiB
Markdown
# Attributes Resolution
|
|
|
|
Resolving attributes means, for example, given the expression `x.y`, determining the type of this entire expression. Therefore, the type of `x` must be determined, but the type of `x` may not be uniquely determined. In such cases, the type of `x.y` may still be determined, or it may fail. This is the problem of attribute resolution that this section deals with.
|
|
|
|
As a simple case, consider the expression `1.real` (`x == 1, y == real`). The type of `1` is `{1}`. `{1}` is a subtype of `Nat`, `Int` and `Obj`. Trace these types in turn to find the definition of `real`. In this case, it is found in `Int` (`Int.real: Int`). Thus the type of `x` is cast to `Int` and the type of `1.real` is `Int`.
|
|
|
|
Thus, if the type of `x` is uniquely determinable, inference proceeds in the order `x` -> `y`.
|
|
However, when the type of `x` cannot be uniquely determined, the type of `x` can be narrowed down from `y`.
|
|
|
|
For example:
|
|
|
|
```erg
|
|
consts c = c.co_consts
|
|
```
|
|
|
|
``co_consts`` is an attribute of type `Code`. This example was chosen just because a name that does not cover others. It is not the essence of what this function means.
|
|
Since the type of ``c`` is not specified, You may think inference is not possible, but it is (when the only type with ``co_consts`` in the namespace is `Code`).
|
|
|
|
Erg assigns a type variable when no variable type is specified.
|
|
|
|
```erg
|
|
consts: ?1
|
|
c: ?2
|
|
```
|
|
|
|
The type inferrer tries to determine the definition of `co_consts` from the type `?2`, but fails because `?2` is a type variable with no condition.
|
|
In such a case, [`get_attr_type_by_name`](https://github.com/erg-lang/erg/blob/b8a87c0591e5603c1afcfc54c073ab2101ff2857/crates/erg_compiler/context/inquire.rs#L2884) is called.
|
|
This method attempts to identify the type `?2` from the name `co_consts`, as opposed to before.
|
|
It succeeds only if the only type with `co_consts` in the namespace is `Code` (or all other types are supertype of Code).
|
|
Erg closes function type checking within a module, so even if a type with `co_consts` is defined outside the module, passing an instance of it to the `consts` function will result in an error (to make this possible, you must use `Structural`, described below). This constraint allows the `consts` function to infer.
|
|
|
|
When a class attribute is defined, the type inferrer keeps track of the "attribute" and "defining class, attribute type" pairs.
|
|
In the case of ``co_consts``, this pair is `{co_consts: {Code, List(Obj, _)}}`.
|
|
|
|
```erg
|
|
method_to_classes: {co_consts: [{Code, List(Obj, _)}], real: [{Int, Int}], times!: [{Nat, (self: Nat, proc!: () => NoneType) => NoneType}], ...}
|
|
```
|
|
|
|
Note that the value of the key-value pair is an array. Only if this array is of length 1, or has the smallest type element, the key is uniquely determined (otherwise a type error will occur).
|
|
|
|
Once the key is identified, the definition type is back-propagated to the type of ``?2``.
|
|
|
|
```erg
|
|
?2(<: Code).co_consts: List(Obj, _)
|
|
```
|
|
|
|
Finally, the type of `consts` is `Code -> List(Obj, _)`.
|
|
|
|
```erg
|
|
consts(c: Code): List(Obj, _) = c.co_consts
|
|
```
|