package interp

import (
	
	
)

// gta performs a global types analysis on the AST, registering types,
// variables and functions symbols at package level, prior to CFG.
// All function bodies are skipped. GTA is necessary to handle out of
// order declarations and multiple source files packages.
// rpath is the relative path to the directory containing the source for the package.
func ( *Interpreter) ( *node, , ,  string) ([]*node, error) {
	 := .initScopePkg(, )
	var  error
	var  []*node

	 := filepath.Base(.fset.Position(.pos).Filename)

	.Walk(func( *node) bool {
		if  != nil {
			return false
		}
		if .scope == nil {
			.scope = 
		}
		switch .kind {
		case constDecl:
			// Early parse of constDecl subtree, to compute all constant
			// values which may be used in further declarations.
			if _,  = .cfg(, , , );  != nil {
				// No error processing here, to allow recovery in subtree nodes.
				// TODO(marc): check for a non recoverable error and return it for better diagnostic.
				 = nil
			}

		case blockStmt:
			if  !=  {
				return false // skip statement block if not the entry point
			}

		case defineStmt:
			var (
				 *itype
				 error
			)
			if .nleft+.nright < len(.child) {
				// Type is declared explicitly in the assign expression.
				if ,  = nodeType(, , .child[.nleft]);  != nil {
					// The type does not exist yet, stash the error and come back
					// when the type is known.
					.meta = 
					 = append(, )
					return false
				}
			}

			var  int
			if .nright > 0 {
				 = len(.child) - .nright
			}

			for  := 0;  < .nleft; ++ {
				,  := .child[], .child[+]
				if isBlank() {
					 = .cfgErrorf("cannot use _ as value")
				}
				 := .rval
				if .anc.kind == constDecl {
					if ,  := .cfg(, , , );  != nil {
						// Constant value can not be computed yet.
						// Come back when child dependencies are known.
						 = append(, )
						return false
					}
				}
				 := 
				if  == nil {
					if ,  = nodeType(, , );  != nil ||  == nil {
						// The type does is not known yet, stash the error and come back
						// when the type is known.
						.meta = 
						 = append(, )
						return false
					}
					 = .rval
				}
				if !.isComplete() {
					// Come back when type is known.
					 = append(, )
					return false
				}
				if .cat == nilT {
					 = .cfgErrorf("use of untyped nil")
					return false
				}
				if .isBinMethod {
					 = valueTOf(.methodCallType(), isBinMethod(), withScope())
				}
				.sym[.ident] = &symbol{kind: varSym, global: true, index: .add(), typ: , rval: , node: }
				if .anc.kind == constDecl {
					.sym[.ident].kind = constSym
					if childPos() == len(.anc.child)-1 {
						.iota = 0
					} else {
						.iota++
					}
				}
			}
			return false

		case defineXStmt:
			 = compDefineX(, )

		case valueSpec:
			 := len(.child) - 1
			if .typ = .child[].typ; .typ == nil {
				if .typ,  = nodeType(, , .child[]);  != nil {
					return false
				}
				if !.typ.isComplete() {
					// Come back when type is known.
					 = append(, )
					return false
				}
			}
			for ,  := range .child[:] {
				 := filepath.Join(.ident, )
				,  := .sym[]
				if ! {
					.sym[.ident] = &symbol{index: .add(.typ), kind: varSym, global: true, typ: .typ, node: }
					continue
				}
				.level = globalFrame

				// redeclaration error
				if .typ.node != nil && .typ.node.anc != nil {
					 := .interp.fset.Position(.typ.node.anc.pos)
					 = .cfgErrorf("%s redeclared in this block\n\tprevious declaration at %v", .ident, )
					return false
				}
				 = .cfgErrorf("%s redeclared in this block", .ident)
				return false
			}

		case funcDecl:
			if .typ,  = nodeType(, , .child[2]);  != nil {
				return false
			}
			 := false
			 := .child[1].ident
			switch {
			case isMethod():
				// Add a method symbol in the receiver type name space
				var  *itype
				.ident = 
				 := .child[0].child[0]
				 := .lastChild()
				,  := .ident, false
				// Identifies the receiver type name. It could be an ident, a
				// generic type (indexExpr), or a pointer on either lasts.
				if  == "" {
					 = .child[0].ident
					switch .kind {
					case starExpr:
						 = true
						switch  := .child[0]; .kind {
						case indexExpr, indexListExpr:
							 = .child[0].ident
							 = true
						}
					case indexExpr, indexListExpr:
						 = true
					}
				}
				, ,  := .lookup()
				if ! {
					.meta = .cfgErrorf("undefined: %s", )
					 = append(, )
					return false
				}
				if .typ.path !=  {
					 = .cfgErrorf("cannot define new methods on non-local type %s", baseType(.typ).id())
					return false
				}
				 = .typ
				if  {
					 := .typ
					 = ptrOf(, withNode(), withScope())
					.incomplete = .incomplete
					.addMethod()
				}
				.addMethod()
				.typ = 
				if .cat == genericT {
					// generate methods for already instantiated receivers
					for ,  := range .instance {
						if  = genMethod(, , , , .node.anc.param);  != nil {
							return false
						}
					}
				}
			case  == "init":
				// init functions do not get declared as per the Go spec.
			default:
				 := filepath.Join(, )
				if ,  := .sym[];  {
					// redeclaration error
					 = .cfgErrorf("%s redeclared in this block", )
					return false
				}
				// Add a function symbol in the package name space except for init
				.sym[] = &symbol{kind: funcSym, typ: .typ, node: , index: -1}
			}
			if !.typ.isComplete() && ! {
				 = append(, )
			}
			return false

		case importSpec:
			var ,  string
			if len(.child) == 2 {
				 = constToString(.child[1].rval)
				 = .child[0].ident
			} else {
				 = constToString(.child[0].rval)
			}
			// Try to import a binary package first, or a source package
			var  string
			if  := path.Base(); path.Dir() ==  {
				 = 
			}
			if  := .binPkg[];  != nil {
				switch  {
				case "_": // no import of symbols
				case ".": // import symbols in current scope
					for ,  := range  {
						 := .Type()
						 := binSym
						if isBinType() {
							 = .Elem()
							 = typeSym
						}
						.sym[] = &symbol{kind: , typ: valueTOf(, withScope()), rval: }
					}
				default: // import symbols in package namespace
					if  == "" {
						 = .pkgNames[]
					}

					// If an incomplete type exists, delete it
					if ,  := .sym[];  && .kind == typeSym && .typ.incomplete {
						delete(.sym, )
					}

					// Imports of a same package are all mapped in the same scope, so we cannot just
					// map them by their names, otherwise we could have collisions from same-name
					// imports in different source files of the same package. Therefore, we suffix
					// the key with the basename of the source file.
					 = filepath.Join(, )
					if ,  := .sym[]; ! {
						.sym[] = &symbol{kind: pkgSym, typ: &itype{cat: binPkgT, path: , scope: }}
						break
					} else if .kind == pkgSym && .typ.cat == srcPkgT && .typ.path ==  {
						// ignore re-import of identical package
						break
					}

					// redeclaration error. Not caught by the parser.
					 = .cfgErrorf("%s redeclared in this block", )
					return false
				}
			} else if ,  = .importSrc(, , NoTest);  == nil {
				.types = .universe.types
				switch  {
				case "_": // no import of symbols
				case ".": // import symbols in current namespace
					for ,  := range .srcPkg[] {
						if canExport() {
							.sym[] = 
						}
					}
				default: // import symbols in package namespace
					if  == "" {
						 = 
					}
					 = filepath.Join(, )
					if ,  := .sym[]; ! {
						.sym[] = &symbol{kind: pkgSym, typ: &itype{cat: srcPkgT, path: , scope: }}
						break
					} else if .kind == pkgSym && .typ.cat == srcPkgT && .typ.path ==  {
						// ignore re-import of identical package
						break
					}

					// redeclaration error
					 = .cfgErrorf("%s redeclared as imported package name", )
					return false
				}
			} else {
				 = .cfgErrorf("import %q error: %v", , )
			}

		case typeSpec, typeSpecAssign:
			if isBlank(.child[0]) {
				 = .cfgErrorf("cannot use _ as value")
				return false
			}
			 := .child[0].ident
			if len(.child) > 2 {
				// Handle a generic type: skip definition as parameter is not instantiated yet.
				.typ = genericOf(nil, , , withNode(.child[0]), withScope())
				if ,  := .sym[]; ! {
					.sym[] = &symbol{kind: typeSym, node: }
				}
				.sym[].typ = .typ
				return false
			}
			var  *itype
			if ,  = nodeType(, , .child[1]);  != nil {
				 = nil
				 = append(, )
				return false
			}

			if .kind == typeSpecAssign {
				// Create an aliased type in the current scope
				.sym[] = &symbol{kind: typeSym, node: , typ: }
				.typ = 
				break
			}

			// else we are not an alias (typeSpec)

			switch .child[1].kind {
			case identExpr, selectorExpr:
				.typ = namedOf(, , , withNode(.child[0]), withScope())
				.typ.incomplete = .incomplete
				.typ.field = .field
				copy(.typ.method, .method)
			default:
				.typ = 
				.typ.name = 
				.typ.path = 
			}
			.typ.str = .typ.path + "." + .typ.name

			 := filepath.Join(, )
			if ,  := .sym[];  {
				// redeclaration error
				 = .cfgErrorf("%s redeclared in this block", )
				return false
			}
			,  := .sym[]
			if ! {
				 = &symbol{kind: typeSym, node: }
				.sym[] = 
			} else if .typ != nil && (len(.typ.method) > 0) {
				// Type has already been seen as a receiver in a method function
				for ,  := range .typ.method {
					.typ.addMethod()
				}
			}
			.typ = .typ
			if !.typ.isComplete() {
				 = append(, )
			}
			return false
		}
		return true
	}, nil)

	if  != .universe {
		.pop()
	}
	return , 
}

func baseType( *itype) *itype {
	for {
		switch .cat {
		case ptrT, linkedT:
			 = .val
		default:
			return 
		}
	}
}

// gtaRetry (re)applies gta until all global constants and types are defined.
func ( *Interpreter) ( []*node, ,  string) error {
	 := []*node{}
	for {
		for ,  := range  {
			,  := .gta(, , , )
			if  != nil {
				return 
			}
			 = append(, ...)
		}

		if len() == 0 || equalNodes(, ) {
			break
		}

		 = 
		 = []*node{}
	}

	if len() > 0 {
		 := [0]
		switch .kind {
		case typeSpec, typeSpecAssign:
			if  := definedType(.typ);  != nil {
				return 
			}
		case defineStmt, funcDecl:
			if ,  := .meta.(error);  {
				return 
			}
		}
		return .cfgErrorf("constant definition loop")
	}
	return nil
}

func definedType( *itype) error {
	if !.incomplete {
		return nil
	}
	switch .cat {
	case interfaceT, structT:
		for ,  := range .field {
			if  := (.typ);  != nil {
				return 
			}
		}
	case funcT:
		for ,  := range .arg {
			if  := ();  != nil {
				return 
			}
		}
		for ,  := range .ret {
			if  := ();  != nil {
				return 
			}
		}
	case mapT:
		if  := (.key);  != nil {
			return 
		}
		fallthrough
	case linkedT, arrayT, chanT, chanSendT, chanRecvT, ptrT, variadicT:
		if  := (.val);  != nil {
			return 
		}
	case nilT:
		return .node.cfgErrorf("undefined: %s", .node.ident)
	}
	return nil
}

// equalNodes returns true if two slices of nodes are identical.
func equalNodes(,  []*node) bool {
	if len() != len() {
		return false
	}
	for ,  := range  {
		if  != [] {
			return false
		}
	}
	return true
}