class
Crystal::MainVisitor
Overview
This is the main visitor of the program, ran after types have been declared
and their type declarations (like @x : Int32) have been processed.
This visits the "main" code of the program and resolves calls, instantiates methods and visits them, recursively, with other MainVisitors.
The visitor keeps track of a method's variables (or the main program, split into several files, in case of top-level code). It keeps track both of the type of a variable at a single point (stored in @vars) and the combined type of all assignments to it (in @meta_vars).
Call resolution logic is in Call#recalculate, where method lookup is done.
Direct Known Subclasses
Defined in:
compiler/crystal/semantic/main_visitor.crConstant Summary
-
ValidClassVarAnnotations =
["ThreadLocal"] of ::String -
ValidGlobalAnnotations =
["ThreadLocal"] of ::String
Constructors
- .new(program, vars = MetaVars.new, typed_def = nil, meta_vars = nil)
- .new(*, from_main_visitor : MainVisitor)
Class Method Summary
- .check_automatic_cast(program, value, var_type, assign = nil)
- .check_type_allowed_as_proc_argument(node, type)
Instance Method Summary
- #all_exception_handler_vars : Array(MetaVars) | Nil
- #all_exception_handler_vars=(all_exception_handler_vars : Array(MetaVars) | Nil)
- #assign_to_meta_var(name, context = current_context)
- #bind_block_var(node, target, meta_vars, before_block_vars)
- #bind_initialize_instance_vars(owner)
- #bind_meta_var(var : Var)
- #bind_vars(from_vars, to_vars, ignored = nil)
- #block : Block | Nil
- #block=(block)
- #block_nest : Int32
- #block_nest=(block_nest : Int32)
- #call : Call | Nil
- #call=(call : Call | Nil)
- #call_needs_splat_expansion?(node)
-
#check_automatic_cast(value, var_type, assign = nil)
See if we can automatically cast the value if the types don't exactly match
-
#check_call_in_initialize(node)
Checks if it's a call to self.
- #check_closured(var, mark_as_mutably_closured : Bool = false)
- #check_exception_handler_vars(var_name, node)
- #check_initialize_instance_vars_types(owner)
-
#check_lib_call(node, obj_type)
Fill function literal argument types for C functions
- #check_lib_call_arg(method, arg_index, &)
-
#check_mutably_closured(meta_var, var)
If the meta_var is closured but not readonly, then bind var to it (it gets all types assigned to meta_var).
- #check_not_a_constant(node)
- #check_self_closured
- #check_slice_literal_call(node, obj_type)
-
#check_special_new_call(node, obj_type)
Checks if it's ProcType#new
-
#check_super_or_previous_def_in_initialize(node)
If it's a super or previous_def call inside an initialize we treat set instance vars from superclasses to not-nil.
- #check_type_in_type_args(type)
- #closure_context
- #conditional_no_return(node, var)
- #convert_struct_or_union_numeric_argument(node, unaliased_type, expected_type, actual_type)
- #current_context
- #current_non_block_context
- #define_special_var(name, value)
- #end_visit(node : Splat)
- #end_visit(node : DoubleSplat)
- #end_visit(node : RespondsTo)
- #end_visit(node : Break)
- #end_visit(node : Next)
- #end_visit(node : TupleLiteral)
- #end_visit(node : NamedTupleLiteral)
- #expand(node)
- #expand(node, &)
- #expand_named(node, generic_type)
- #file_module : FileModule | Nil
- #file_module=(file_module : FileModule | Nil)
-
#filter_vars(filters)
If we have:
- #filter_vars(filters, &)
- #first_time_accessing_meta_type_var?(var)
-
#free_vars
Returns free variables
- #fun_literal_context : Def | Program | Nil
- #fun_literal_context=(fun_literal_context : Def | Program | Nil)
- #gather_instance_vars_read(node)
-
#get_expression_var(exp)
Get the variable of an expression.
- #handle_unpacked_block_argument(node, arg, meta_vars, before_block_vars, ignored_vars_after_block)
- #ignoring_type_filters(&)
- #inside_block?
- #inside_constant=(inside_constant : Bool)
- #inside_constant? : Bool
- #inside_ensure=(inside_ensure : Bool)
- #inside_ensure? : Bool
- #inside_while?
- #is_initialize : Bool
- #is_initialize=(is_initialize : Bool)
-
#last_block_kind : BlockKind
It means the last block kind.
-
#last_block_kind=(last_block_kind : BlockKind)
It means the last block kind.
- #lookup_class_var(node)
- #lookup_similar_class_variable_name(node, owner)
- #lookup_similar_var_name(name)
- #lookup_var_or_instance_var(var : Var)
- #lookup_var_or_instance_var(var : InstanceVar)
- #lookup_var_or_instance_var(var)
- #mark_as_closured(var, var_context, mark_as_mutably_closured : Bool)
- #match_context : MatchContext | Nil
- #match_context=(match_context : MatchContext | Nil)
- #merge_if_var(name, node, cond_vars, then_var, else_var, before_then_vars, before_else_vars, then_unreachable, else_unreachable)
-
#merge_if_vars(node, cond_vars, then_vars, else_vars, before_then_vars, before_else_vars, then_unreachable, else_unreachable)
Here we merge the variables from both branches of an if.
- #merge_rescue_vars(body_vars, all_rescue_vars)
-
#merge_while_vars(endless, before_cond_vars_copy, before_cond_vars, after_cond_vars, after_cond_vars_nil_if_read, all_break_vars)
Here we assign the types of variables after a while.
-
#meta_vars : MetaVars
Here we store the cumulative types of variables as we traverse the nodes.
- #needs_type_filters?
- #new_meta_var(name, context = current_context)
-
#node_exp_or_nil_literal(node)
Returns node.exp if it's not nil.
- #parent : MainVisitor | Nil
- #parent=(parent : MainVisitor | Nil)
- #path_lookup : Crystal::Type?
- #path_lookup=(path_lookup)
- #pointerof_var(node)
- #prepare_call(node)
- #recalculate_call(node : Call)
- #replace_call_splats(node)
- #request_type_filters(&)
-
#special_c_struct_or_union_new_with_named_args(node, type, named_args)
Rewrite:
- #the_self(node)
- #type_assign(target : Var, value, node, restriction = nil)
- #type_assign(target : InstanceVar, value, node)
- #type_assign(target : Path, value, node)
- #type_assign(target : Global, value, node)
- #type_assign(target : ClassVar, value, node)
- #type_assign(target : Underscore, value, node)
- #type_assign(target, value, node)
- #typed_def
- #typed_def? : Crystal::Def?
- #undefined_class_variable(node, owner)
- #undefined_instance_variable(owner, node)
- #untyped_def : Def
- #untyped_def=(untyped_def : Def)
- #untyped_def=(untyped_def)
- #untyped_def? : Def | Nil
-
#vars : Hash(String, Crystal::MetaVar)
In vars we store the types of variables as we traverse the nodes.
- #visit(node : FileNode)
- #visit(node : Path)
- #visit(node : Generic)
- #visit(node : ProcNotation)
- #visit(node : Union)
- #visit(node : Metaclass)
- #visit(node : Self)
- #visit(node : Var)
- #visit(node : TypeDeclaration)
- #visit(node : UninitializedVar)
- #visit(node : Out)
- #visit(node : Global)
- #visit(node : InstanceVar)
- #visit(node : ReadInstanceVar)
- #visit(node : ClassVar)
- #visit(node : Expressions)
- #visit(node : Assign)
- #visit(node : Yield)
- #visit(node : Block)
- #visit(node : ProcLiteral)
- #visit(node : ProcPointer)
- #visit(node : Call)
- #visit(node : Return)
- #visit(node : Underscore)
- #visit(node : IsA)
- #visit(node : Cast | NilableCast)
- #visit(node : FunDef)
- #visit(node : If)
- #visit(node : While)
- #visit(node : TupleIndexer)
- #visit(node : Primitive)
- #visit(node : PointerOf)
- #visit(node : TypeOf)
- #visit(node : SizeOf)
- #visit(node : InstanceSizeOf)
- #visit(node : AlignOf)
- #visit(node : InstanceAlignOf)
- #visit(node : OffsetOf)
- #visit(node : Rescue)
- #visit(node : ExceptionHandler)
- #visit(node : Asm)
- #visit(node : Nop)
- #visit(node : NilLiteral)
- #visit(node : BoolLiteral)
- #visit(node : NumberLiteral)
- #visit(node : CharLiteral)
- #visit(node : SymbolLiteral)
- #visit(node : StringLiteral)
- #visit(node : RegexLiteral)
- #visit(node : ArrayLiteral)
- #visit(node : HashLiteral)
- #visit(node : And)
- #visit(node : Or)
- #visit(node : RangeLiteral)
- #visit(node : StringInterpolation)
- #visit(node : Case)
- #visit(node : Select)
- #visit(node : MultiAssign)
- #visit(node : Not)
- #visit(node : VisibilityModifier)
- #visit(node : Arg)
- #visit(node : AssignWithRestriction)
- #visit(node : Unreachable)
- #visit(node : When | Unless | Until | MacroLiteral | OpAssign)
- #visit(node : ImplicitObj)
- #visit_allocate(node)
- #visit_any(node)
- #visit_class_var(node)
- #visit_pointer_malloc(node)
- #visit_pointer_new(node)
- #visit_pointer_set(node)
- #visit_pre_initialize(node)
- #visit_read_instance_var(node)
- #visit_struct_or_union_set(node)
- #visit_va_arg(node)
- #with_block_kind(kind : BlockKind, &)
- #with_scope : Type | Nil
- #with_scope=(with_scope : Type | Nil)
- #yield_vars : Array(Var) | Nil
- #yield_vars=(yield_vars : Array(Var) | Nil)
Instance methods inherited from class Crystal::SemanticVisitor
check_allowed_in_lib(node, type = node.type.instance_type)
check_allowed_in_lib,
check_class_var_annotations
check_class_var_annotations,
check_declare_var_type(node, declared_type, variable_kind)
check_declare_var_type,
check_outside_exp(node, op)
check_outside_exp,
class_var_owner(node)
class_var_owner,
current_type : ModuleType
current_type,
current_type=(current_type : ModuleType)
current_type=,
end_visit_any(node)
end_visit_any,
eval_macro(node, &)
eval_macro,
expand_inline_macro(node, mode = nil, accept = true)
expand_inline_macro,
expand_macro(node, raise_on_missing_const = true, first_pass = false, accept = true)expand_macro(the_macro, node, mode = nil, *, visibility : Visibility, accept = true, &) expand_macro, expand_macro_argument(node, expansion_scope) expand_macro_argument, expand_macro_arguments(call, expansion_scope) expand_macro_arguments, free_vars : Hash(String, TypeVar) | Nil free_vars, inside_exp? inside_exp?, interpret_enum_value(node : ASTNode, target_type : IntegerType | Nil = nil) interpret_enum_value, lookup_annotation(ann) lookup_annotation, lookup_type(node : ASTNode, free_vars = nil, find_root_generic_type_parameters = true) lookup_type, nesting_exp?(node) nesting_exp?, process_annotations(annotations, &) process_annotations, program : Program program, pushing_type(type : ModuleType, &) pushing_type, read_annotations read_annotations, scope : Type scope, scope=(scope : Type)
scope=(scope : Crystal::Type | Nil) scope=, scope? : Type | Nil scope?, validate_annotation(annotation_type, ann) validate_annotation, vars : MetaVars vars, vars=(vars : MetaVars) vars=, visit(node : Require)
visit(node : ClassDef)
visit(node : ModuleDef)
visit(node : AnnotationDef)
visit(node : EnumDef)
visit(node : LibDef)
visit(node : Include)
visit(node : Extend)
visit(node : Alias)
visit(node : Def)
visit(node : Macro)
visit(node : Annotation)
visit(node : Call)
visit(node : MacroExpression)
visit(node : MacroIf)
visit(node : MacroFor)
visit(node : MacroVerbatim)
visit(node : ExternalVar | Path | Generic | ProcNotation | Union | Metaclass | Self | TypeOf)
visit(node : ASTNode) visit, visit_any(node) visit_any
Constructor methods inherited from class Crystal::SemanticVisitor
new(program : Crystal::Program, vars : Hash(String, Crystal::MetaVar) = MetaVars.new)
new
Instance methods inherited from class Crystal::Visitor
accept(node)
accept,
end_visit(node)
end_visit,
end_visit_any(node)
end_visit_any,
visit_any(node)
visit_any
Instance methods inherited from class Reference
==(other : self)==(other : JSON::Any)
==(other : YAML::Any)
==(other) ==, dup dup, hash(hasher) hash, initialize initialize, inspect(io : IO) : Nil inspect, object_id : UInt64 object_id, pretty_print(pp) : Nil pretty_print, same?(other : Reference) : Bool
same?(other : Nil) same?, to_s(io : IO) : Nil to_s
Constructor methods inherited from class Reference
new
new,
unsafe_construct(address : Pointer, *args, **opts) : self
unsafe_construct
Class methods inherited from class Reference
pre_initialize(address : Pointer)
pre_initialize
Instance methods inherited from class Object
! : Bool
!,
!=(other)
!=,
!~(other)
!~,
==(other)
==,
===(other : JSON::Any)===(other : YAML::Any)
===(other) ===, =~(other) =~, as(type : Class) as, as?(type : Class) as?, class class, dup dup, hash(hasher)
hash hash, in?(collection : Object) : Bool
in?(*values : Object) : Bool in?, inspect(io : IO) : Nil
inspect : String inspect, is_a?(type : Class) : Bool is_a?, itself itself, nil? : Bool nil?, not_nil!(message)
not_nil! not_nil!, pretty_inspect(width = 79, newline = "\n", indent = 0) : String pretty_inspect, pretty_print(pp : PrettyPrint) : Nil pretty_print, responds_to?(name : Symbol) : Bool responds_to?, tap(&) tap, to_json(io : IO) : Nil
to_json : String to_json, to_pretty_json(indent : String = " ") : String
to_pretty_json(io : IO, indent : String = " ") : Nil to_pretty_json, to_s(io : IO) : Nil
to_s : String to_s, to_yaml(io : IO) : Nil
to_yaml : String to_yaml, try(&) try, unsafe_as(type : T.class) forall T unsafe_as
Class methods inherited from class Object
from_json(string_or_io : String | IO, root : String)from_json(string_or_io : String | IO) from_json, from_yaml(string_or_io : String | IO) from_yaml
Macros inherited from class Object
class_getter(*names, &block)
class_getter,
class_getter!(*names)
class_getter!,
class_getter?(*names, &block)
class_getter?,
class_property(*names, &block)
class_property,
class_property!(*names)
class_property!,
class_property?(*names, &block)
class_property?,
class_setter(*names)
class_setter,
def_clone
def_clone,
def_equals(*fields)
def_equals,
def_equals_and_hash(*fields)
def_equals_and_hash,
def_hash(*fields)
def_hash,
delegate(*methods, to object)
delegate,
forward_missing_to(delegate)
forward_missing_to,
getter(*names, &block)
getter,
getter!(*names)
getter!,
getter?(*names, &block)
getter?,
property(*names, &block)
property,
property!(*names)
property!,
property?(*names, &block)
property?,
setter(*names)
setter
Constructor Detail
Class Method Detail
Instance Method Detail
See if we can automatically cast the value if the types don't exactly match
Checks if it's a call to self. In that case, all instance variables not mentioned so far will be considered nil.
If the meta_var is closured but not readonly, then bind var to it (it gets all types assigned to meta_var). Otherwise, add it to the local vars so that they could be bond later on, if the meta_var stops being readonly.
If it's a super or previous_def call inside an initialize we treat set instance vars from superclasses to not-nil.
If we have:
if a ... end
then inside the if 'a' must not be nil.
This is what we do here: we create a meta-variable for it and filter it accordingly. This also applied to .is_a? and .responds_to?.
This also applies to 'while' conditions and also to the else part of an if, but with filters inverted.
Get the variable of an expression. If it's a variable, it's that variable. If it's an assignment to a variable, it's that variable.
It means the last block kind. It is used to detect break or next
from ensure.
begin
# `last_block_kind.none?`
ensure
# `last_block_kind.ensure?`
while true
# `last_block_kind.while?`
end
loop do
# `last_block_kind.block?`
end
# `last_block_kind.ensure?`
end
It means the last block kind. It is used to detect break or next
from ensure.
begin
# `last_block_kind.none?`
ensure
# `last_block_kind.ensure?`
while true
# `last_block_kind.while?`
end
loop do
# `last_block_kind.block?`
end
# `last_block_kind.ensure?`
end
Here we merge the variables from both branches of an if. We basically:
- Create a variable whose type is the merged types of the last type of each branch.
- Make the variable nilable if the variable wasn't declared before the 'if' and it doesn't appear in one of the branches.
- Don't use the type of a branch that is unreachable (ends with return, break or with a call that is NoReturn)
Here we assign the types of variables after a while.
Here we store the cumulative types of variables as we traverse the nodes.
Returns node.exp if it's not nil. Otherwise,
creates a NilLiteral node that has the same location
as node, and returns that.
We use this NilLiteral when the user writes
return, next or break without arguments,
so that in the error trace we can show it right
(those expressions have a NoReturn type so we can't
directly bind to them).
Rewrite:
LibFoo::Struct.new arg0: value0, argN: value0
To:
temp = LibFoo::Struct.new temp.arg0 = value0 temp.argN = valueN temp
In vars we store the types of variables as we traverse the nodes.
These type are not cumulative: if you do x = 1, 'x' will have
type Int32. Then if you do x = false, 'x' will have type Bool.