	// and use node type instead of function return type.
	//
	// If node type is anyType, then we should use function
	// return type. For example, on error we return anyType
	// for a call `errCall().Method()` and method will be
	// evaluated on `anyType.Method()`, so return type will
	// be anyType `anyType.Method(): anyType`. Patcher can
	// fix `errCall()` to return proper type, so on second
	// checker pass we should replace anyType on method node
	// with new correct function return type.
	if typ := node.Type(); typ != nil && typ != anyType {
		return *node.Nature()
	}

	// $env is not callable.
	if id, ok := node.Callee.(*ast.IdentifierNode); ok && id.Value == "$env" {
		return v.error(node, "%s is not callable", v.config.Env.String())
	}

	nt := v.visit(node.Callee)
	if nt.IsUnknown(&v.config.NtCache) {
		return Nature{}
	}

	if nt.TypeData != nil && nt.TypeData.Func != nil {
		return v.checkFunction(nt.TypeData.Func, node, node.Arguments)
	}

	fnName := "function"
	if identifier, ok := node.Callee.(*ast.IdentifierNode); ok {
		fnName = identifier.Value
	}
	if member, ok := node.Callee.(*ast.MemberNode); ok {
		if name, ok := member.Property.(*ast.StringNode); ok {
			fnName = name.Value
		}
	}

	if nt.Nil {
		return v.error(node, "%v is nil; cannot call nil as function", fnName)
	}

	if nt.Kind == reflect.Func {
		outType, err := v.checkArguments(fnName, nt, node.Arguments, node)
		if err != nil {
			if v.err == nil {
				v.err = err
			}
			return Nature{}
		}
		return outType
	}
	return v.error(node, "%s is not callable", nt.String())
}

func (v *Checker) builtinNode(node *ast.BuiltinNode) Nature {
	switch node.Name {
	case "all", "none", "any", "one":
		collection := v.visit(node.Arguments[0])
		collection = collection.Deref(&v.config.NtCache)
		if !collection.IsArray() && !collection.IsUnknown(&v.config.NtCache) {
			return v.error(node.Arguments[0], "builtin %v takes only array (got %v)", node.Name, collection.String())
		}

		v.begin(collection)
		predicate := v.visit(node.Arguments[1])
		v.end()

		if predicate.IsFunc() &&
			predicate.NumOut() == 1 &&
			predicate.NumIn() == 1 && predicate.IsFirstArgUnknown(&v.config.NtCache) {

			predicateOut := predicate.Out(&v.config.NtCache, 0)
			if !predicateOut.IsBool() && !predicateOut.IsUnknown(&v.config.NtCache) {
				return v.error(node.Arguments[1], "predicate should return boolean (got %s)", predicateOut.String())
			}
			return v.config.NtCache.FromType(boolType)
		}
		return v.error(node.Arguments[1], "predicate should has one input and one output param")

	case "filter":
		collection := v.visit(node.Arguments[0])
		collection = collection.Deref(&v.config.NtCache)
		if !collection.IsArray() && !collection.IsUnknown(&v.config.NtCache) {
			return v.error(node.Arguments[0], "builtin %v takes only array (got %v)", node.Name, collection.String())
		}

		v.begin(collection)
		predicate := v.visit(node.Arguments[1])
		v.end()

		if predicate.IsFunc() &&
			predicate.NumOut() == 1 &&
			predicate.NumIn() == 1 && predicate.IsFirstArgUnknown(&v.config.NtCache) {

			predicateOut := predicate.Out(&v.config.NtCache, 0)
			if !predicateOut.IsBool() && !predicateOut.IsUnknown(&v.config.NtCache) {
				return v.error(node.Arguments[1], "predicate should return boolean (got %s)", predicateOut.String())
			}
			if collection.IsUnknown(&v.config.NtCache) {
				return v.config.NtCache.FromType(arrayType)
			}
			collection = collection.Elem(&v.config.NtCache)
			return collection.MakeArrayOf(&v.config.NtCache)
		}
		return v.error(node.Arguments[1], "predicate should has one input and one output param")

	case "map":
		collection := v.visit(node.Arguments[0])
		collection = collection.Deref(&v.config.NtCache)
		if !collection.IsArray() && !collection.IsUnknown(&v.config.NtCache) {
			return v.error(node.Arguments[0], "builtin %v takes only array (got %v)", node.Name, collection.String())
		}

		v.begin(collection, varScope{"index", v.config.NtCache.FromType(intType)})
		predicate := v.visit(node.Arguments[1])
		v.end()

		if predicate.IsFunc() &&
			predicate.NumOut() == 1 &&
			predicate.NumIn() == 1 && predicate.IsFirstArgUnknown(&v.config.NtCache) {
	for i := len(v.varScopes) - 1; i >= 0; i-- {
		if v.varScopes[i].name == node.Name {
			return v.error(node, "cannot redeclare variable %v", node.Name)
		}
	}
	varNature := v.visit(node.Value)
	v.varScopes = append(v.varScopes, varScope{node.Name, varNature})
	exprNature := v.visit(node.Expr)
	v.varScopes = v.varScopes[:len(v.varScopes)-1]
	return exprNature
}

func (v *Checker) sequenceNode(node *ast.SequenceNode) Nature {
	if len(node.Nodes) == 0 {
		return v.error(node, "empty sequence expression")
	}
	var last Nature
	for _, node := range node.Nodes {
		last = v.visit(node)
	}
	return last
}

func (v *Checker) conditionalNode(node *ast.ConditionalNode) Nature {
	c := v.visit(node.Cond)
	c = c.Deref(&v.config.NtCache)
	if !c.IsBool() && !c.IsUnknown(&v.config.NtCache) {
		return v.error(node.Cond, "non-bool expression (type %v) used as condition", c.String())
	}

	t1 := v.visit(node.Exp1)
	t2 := v.visit(node.Exp2)

	if t1.Nil && !t2.Nil {
		return t2
	}
	if !t1.Nil && t2.Nil {
		return t1
	}
	if t1.Nil && t2.Nil {
		return v.config.NtCache.NatureOf(nil)
	}
	if t1.AssignableTo(t2) {
		if t1.IsArray() && t2.IsArray() {
			e1 := t1.Elem(&v.config.NtCache)
			e2 := t2.Elem(&v.config.NtCache)
			if !e1.AssignableTo(e2) || !e2.AssignableTo(e1) {
				return v.config.NtCache.FromType(arrayType)
			}
		}
		return t1
	}
	return Nature{}
}

func (v *Checker) arrayNode(node *ast.ArrayNode) Nature {
	var prev Nature
	allElementsAreSameType := true
	for i, node := range node.Nodes {
		curr := v.visit(node)
		if i > 0 {
