当前位置:网站首页>Go编译原理系列6(类型检查)
Go编译原理系列6(类型检查)
2022-08-05 10:09:00 【InfoQ】
6. Go编译过程-类型检查
前言
- 常量、类型和函数名及类型验证
- 变量的赋值和初始化
- 计算编译时的常量、将声明与标识符绑定
- 会对一些内置函数进行改写(下边介绍源码时会提到)
- 哈希键值对的类型
- 做特别的语法或语义检查(引用的结构体字段是否是大写可导出的?数组字面量的访问是否超过了其长度?数组的索引是不是正整数?)
类型检查整体概览
var test int
test := 1
Go的编译入口文件:src/cmd/compile/main.go -> gc.Main(archInit)
func Main(archInit func(*Arch)) {
......
lines := parseFiles(flag.Args())//词法分析、语法分析、抽象语法树构建都在这里
......
//开始遍历抽象语法树,对每个结点进行类型检查
for i := 0; i < len(xtop); i++ {
n := xtop[i]
if op := n.Op; op != ODCL && op != OAS && op != OAS2 && (op != ODCLTYPE || !n.Left.Name.Param.Alias()) {
xtop[i] = typecheck(n, ctxStmt)
}
}
for i := 0; i < len(xtop); i++ {
n := xtop[i]
if op := n.Op; op == ODCL || op == OAS || op == OAS2 || op == ODCLTYPE && n.Left.Name.Param.Alias() {
xtop[i] = typecheck(n, ctxStmt)
}
}
......
checkMapKeys()//对哈希中键的类型进行检查
......
}
typecheck
typecheck
typecheck1
func typecheck1(n *Node, top int) (res *Node) {
......
switch n.Op {
// until typecheck is complete, do nothing.
default:
Dump("typecheck", n)
Fatalf("typecheck %v", n.Op)
// names
case OLITERAL:
ok |= ctxExpr
if n.Type == nil && n.Val().Ctype() == CTSTR {
n.Type = types.UntypedString
}
case ONONAME:
ok |= ctxExpr
case ONAME:
......
case OTARRAY:
......
case OTMAP:
......
}
......
}
深入了解类型检查
OAS:赋值语句
// Left = Right or (if Colas=true) Left := Right
// If Colas, then Ninit includes a DCL node for Left.
OAS
typecheckas
case OAS:
ok |= ctxStmt
typecheckas(n)
// Code that creates temps does not bother to set defn, so do it here.
if n.Left.Op == ONAME && n.Left.IsAutoTmp() {
n.Left.Name.Defn = n
}
typecheckas
func typecheckas(n *Node) {
......
if n.Left.Name != nil && n.Left.Name.Defn == n && n.Left.Name.Param.Ntype == nil {
n.Right = defaultlit(n.Right, nil)
n.Left.Type = n.Right.Type
}
......
}
OTARRAY:切片
OTARRAY // []int, [8]int, [N]int or [...]int
case OTARRAY:
ok |= ctxType
r := typecheck(n.Right, ctxType)
if r.Type == nil {
n.Type = nil
return n
}
- []int:直接调用
t = types.NewSlice(r.Type)
,返回了一个 TSLICE
类型的结构体,元素的类型信息也会存储在结构体中
- [...]int:交由
typecheckcomplit
方法处理,
func typecheckcomplit(n *Node) (res *Node) {
......
// Need to handle [...]T arrays specially.
if n.Right.Op == OTARRAY && n.Right.Left != nil && n.Right.Left.Op == ODDD {
n.Right.Right = typecheck(n.Right.Right, ctxType)
if n.Right.Right.Type == nil {
n.Type = nil
return n
}
elemType := n.Right.Right.Type
length := typecheckarraylit(elemType, -1, n.List.Slice(), "array literal")
n.Op = OARRAYLIT
n.Type = types.NewArray(elemType, length)
n.Right = nil
return n
}
......
}
[types.NewArray](https://draveness.me/golang/tree/cmd/compile/internal/types.NewArray)
- [6]int:如果在声明切片时,带了数组的大小,则直接调用
[types.NewArray](https://draveness.me/golang/tree/cmd/compile/internal/types.NewArray)
初始化一个存储着数组中元素类型和数组大小的结构体
setTypeNode(n, t)
n.Left = nil
n.Right = nil
checkwidth(t)
OTMAP:map(哈希)
OTMAP // map[string]int
TMAP
case OTMAP:
ok |= ctxType
n.Left = typecheck(n.Left, ctxType)
n.Right = typecheck(n.Right, ctxType)
l := n.Left
r := n.Right
if l.Type == nil || r.Type == nil {
n.Type = nil
return n
}
if l.Type.NotInHeap() {
yyerror("incomplete (or unallocatable) map key not allowed")
}
if r.Type.NotInHeap() {
yyerror("incomplete (or unallocatable) map value not allowed")
}
setTypeNode(n, types.NewMap(l.Type, r.Type))
mapqueue = append(mapqueue, n) // check map keys when all types are settled
n.Left = nil
n.Right = nil
......
func NewMap(k, v *Type) *Type {
t := New(TMAP)
mt := t.MapType()
mt.Key = k
mt.Elem = v
return t
}
func checkMapKeys() {
for _, n := range mapqueue {
k := n.Type.MapType().Key
if !k.Broke() && !IsComparable(k) {
yyerrorl(n.Pos, "invalid map key type %v", k)
}
}
mapqueue = nil
}
OMAKE:make
OMAKE // make(List) (before type checking converts to one of the following)
OMAKECHAN // make(Type, Left) (type is chan)
OMAKEMAP // make(Type, Left) (type is map)
OMAKESLICE // make(Type, Left, Right) (type is slice)
make slice:OMAKESLICE
make map:OMAKEMAP
make chan:OMAKECHAN
case OMAKE:
ok |= ctxExpr
args := n.List.Slice()
......
l := args[0]
l = typecheck(l, ctxType)
t := l.Type
......
i := 1
switch t.Etype {
default:
yyerror("cannot make type %v", t)
n.Type = nil
return n
case TSLICE:
......
n.Left = l
n.Right = r
n.Op = OMAKESLICE
case TMAP:
......
n.Op = OMAKEMAP
case TCHAN:
......
n.Op = OMAKECHAN
}
n.Type = t
- 如果第一个参数是切片类型:获取切片的长度(len)和容量(cap),然后对len和cap进行合法性校验。并且改写了节点的类型
- 如果第一个参数是map类型:获取make的第二个参数,如果没有,则默认设置为0(map的大小)。并且改写节点的类型
- 如果第一个参数是chan类型:获取make的第二个参数,如果没有,则默认设置为0(chan的缓冲区大小)。并且改写节点的类型
总结
参考
- go-ast-book
- 《Go语言底层原理剖析》
- 面向信仰编程-类型检查
边栏推荐
- 第八章:activiti多用户任务分配
- egg框架使用(二)
- Complete image segmentation efficiently based on MindSpore and realize Dice!
- 手写柯里化 - toString 理解
- 2022 Huashu Cup Mathematical Modeling Ideas Analysis and Exchange
- NowCoderTOP35-40——持续更新ing
- [Unity] [UGUI] [Display text on the screen]
- Egg framework usage (1)
- 入门 Polkadot 平行链开发,看这一篇就够了
- 无题十一
猜你喜欢
What is SPL?
气象数据数据处理实例——matlab字符串切割匹配与R语言日期匹配(数据拼接)
Still looking for a network backup resources?Hurry up to collect the following network backup resource search artifact it is worth collecting!
多线程(进阶) - 2.5w字总结
Oracle临时表空间作用
Complete image segmentation efficiently based on MindSpore and realize Dice!
Seata source code analysis: initialization process of TM RM client
Bias lock/light lock/heavy lock lock is healthier. How is locking and unlocking accomplished?
MySQL transactions
阿里顶级架构师多年总结的JVM宝典,哪里不会查哪里!
随机推荐
hcip BGP 增强实验
第五章:多线程通信—wait和notify
无题七
E-sports, convenience, efficiency, security, key words for OriginOS functions
dotnet OpenXML 解析 PPT 图表 面积图入门
[Android]如何使用RecycleView in Kotlin project
Microservice Technology Stack
Huawei's lightweight neural network architecture GhostNet has been upgraded again, and G-GhostNet (IJCV22) has shown its talents on the GPU
Still looking for a network backup resources?Hurry up to collect the following network backup resource search artifact it is worth collecting!
LeetCode 216. Combined Sum III (2022.08.04)
What is the function of the regular expression replaceAll() method?
Oracle 19.3 restart 环境
手写柯里化 - toString 理解
创建一个 Dapp,为什么要选择波卡?
High-quality DeFi application building guide to help developers enjoy DeFi Summer
公众号如何运维?公众号运维专业团队
仿SBUS与串口数据固定转换
Oracle temporary table space role
第八章:activiti多用户任务分配
MySQL使用聚合函数可以不搭配GROUP BY分组吗?