class Myst::TypeCheck::MainVisitor

Defined in:

typecheck/visitors/main_visitor.cr

Instance Method Summary

Instance methods inherited from class Myst::TypeCheck::SemanticVisitor

env : Environment env, env=(env : Environment) env=, visit(node : Node) visit

Constructor methods inherited from class Myst::TypeCheck::SemanticVisitor

new(env : Environment) new

Instance Method Detail

def visit(node : Nop) #

[View source]
def visit(node : Expressions) #

[View source]
def visit(node : NilLiteral) #

[View source]
def visit(node : BooleanLiteral) #

[View source]
def visit(node : IntegerLiteral) #

[View source]
def visit(node : FloatLiteral) #

[View source]
def visit(node : StringLiteral) #

[View source]
def visit(node : InterpolatedStringLiteral) #

[View source]
def visit(node : SymbolLiteral) #

[View source]
def visit(node : ListLiteral) #

[View source]
def visit(node : MapLiteral) #

[View source]
def visit(node : MagicConst) #

[View source]
def visit(node : FunctionCapture) #

[View source]
def visit(node : StaticAssignable) #

def visit(node : Const | Var | Underscore)


[View source]
def visit(node : IVar) #

Until instance variables can have type restrictions applied to them, we can't effectively/efficiently declare an exact type for them, because their mutations are not directly trackable from the AST. So, at least for now, they are always just typed as generic Objects.

This is a bit of a cop-out, but is arguably nicer than trying to determine some exact typing that isn't helpfully-accurate at the time of use.


[View source]
def visit(node : ValueInterpolation) #

[View source]
def visit(node : Self) #

[View source]
def visit(node : SimpleAssign) #

[View source]
def visit(node : MatchAssign) #

[View source]
def visit(node : OpAssign) #

Since OpAssigns are generally just a shorthand from a op= b to either a = a op b or a op a = b, the typechecker can do a syntactic replacement to get the same result.


[View source]
def visit(node : When | Unless) #

Merging of conditional scopes is complex. For any given variable, if every clause in the conditional has an assignment to it, then the final type of that variable is simply the union of those types.

However, if any clause does not assign that variable, then the resulting type must also be unionized with Nil. Also, if the variable is newly created within the conditional, and is not guaranteed an assignment (e.g., by an else clause), then the final type is inherently nilable and must be unioned.

Thankfully, all of these cases are handled by the combination of the unionize and nilify options of Scope#merge!.


[View source]
def visit(node : While) #

Looping expressions can potentially change the typings resolved in their bodies on each iteration. To deal with this, the inferrer itself loops over the expression until the types generated by two consecutive iterations are equal. At this point, all possible type values must have been seen and the typing can be applied.

The resulting type of the loop expression is the type of the last expression in the body after the final iteration, unioned with Nil for the case that the loop is never visited.


[View source]
def visit(node : Until) #

[View source]
def visit(node : Or) #

Boolean logic operations have some nuance to their typing. For example, in an Or, if the first expression is nilable, but the second is not, then the resulting type is guaranteed to not be Nil, so that can be removed from the union.

Since the only possible falsey values are false and nil, if the type of the left hand side does not include Boolean or Nil, the type will be further restricted (to the left-hand-side only for Or, and to the right-hand side for And).


[View source]
def visit(node : And) #

[View source]
def visit(node : Not) #

Not is currently an overrideable boolean operator, though in practice it is not overridden, and even when it is it should return a boolean. This could be expanded to work like a normal Call, or maybe the language will change to disallow overriding the operator.

NOTE This is an intentional deviation from the current behavior of Myst itself. Realistically, allowing the override of the Not operation (!expr) is not useful and causes unnecessary confusion and inconsistency when done in multiple places.


[View source]
def visit(node : Negation) #

[View source]
def visit(node : Splat) #

[View source]
def visit(node : Call) #

[View source]
def visit(node : Block) #

[View source]
def visit(node : Def) #

[View source]
def visit(node : AnonymousFunction) #

[View source]
def visit(node : Match) #

[View source]
def visit(node : ModuleDef) #

[View source]
def visit(node : TypeDef) #

[View source]
def visit(node : TypeUnion) #

[View source]
def visit(node : Instantiation) #

[View source]
def visit(node : Require) #

[View source]
def visit(node : Return) #

Return expressions have no immediate resulting type as they cause the flow of execution to break. Instead, the type gets added to the current return_type of the Environment to be considered as part of the enclosing clause's return type.


[View source]
def visit(node : Next) #

Next is currently a semantic equivalent of Return.


[View source]