erg/doc/EN/compiler/attribute_resolution.md
2024-04-04 23:24:07 +09:00

3 KiB

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:

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.

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 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, _)}}.

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.

?2(<: Code).co_consts: List(Obj, _)

Finally, the type of consts is Code -> List(Obj, _).

consts(c: Code): List(Obj, _) = c.co_consts