![Go语言底层原理剖析](https://wfqqreader-1252317822.image.myqcloud.com/cover/131/40795131/b_40795131.jpg)
3.3 类型推断原理
类型推断依赖编译器的处理能力,编译器执行的过程为:词法解析→语法分析→抽象语法树构建→类型检查→中间代码→代码优化→生成机器码。编译阶段的代码位于go/src/cmd/compile文件中(更详细的过程请查看第1章)。
3.3.1 词法解析与语法分析阶段
在词法解析阶段,会将赋值语句右边的常量解析为一个未定义的类型,例如,ImagLit代表复数,FloatLit代表浮点数,IntLit代表整数。
![](https://epubservercos.yuewen.com/88BA42/21190707608528606/epubprivate/OEBPS/Images/41662_61_5.jpg?sign=1738833761-qFE3kBR80GyAS4TWCrjeAugH70vpp3NW-0-f13ce5919297bdeaef1a0ada2e21af55)
![](https://epubservercos.yuewen.com/88BA42/21190707608528606/epubprivate/OEBPS/Images/41662_62_1.jpg?sign=1738833761-xIimEtzh0lIvt3ZgjgfqzSdzADmWU5ci-0-2e6ef23f18256231bd74c4e26fb6e469)
Go语言源代码采用UTF-8的编码方式,在进行词法解析时,当遇到需要赋值的常量操作时,会逐个读取后面常量的UTF-8字符。字符串的首字符为",数字的首字符为'0'~'9'。具体实现位于syntax.next函数中。
![](https://epubservercos.yuewen.com/88BA42/21190707608528606/epubprivate/OEBPS/Images/41662_62_2.jpg?sign=1738833761-E6DMQ9uCExxZKUL5AdNFLxqSrjuGJ8Ws-0-a8946708a4195bb2fe7f117b93c232b7)
因此对于整数、小数等常量的识别就显得非常简单。如图3-2所示,整数就是字符中全是0~9的数字,浮点数就是字符中有“.”号的数字,字符串的首字符为"或'。
![](https://epubservercos.yuewen.com/88BA42/21190707608528606/epubprivate/OEBPS/Images/41662_62_3.jpg?sign=1738833761-E9uqEfG38xGO32Mf5vUC4ndDKxrVA3Lb-0-151468276f5e19d25d2f43bdc9479223)
图3-2 词法解析阶段解析未定义的常量示例
下面列出的number函数为语法分析阶段处理数字的具体实现。数字首先会被分为小数部分与整数部分,通过字符.进行区分。如果整数部分是以0开头的,则可能有不同的含义,例如0x代表十六进制数、0b代表二进制数。
![](https://epubservercos.yuewen.com/88BA42/21190707608528606/epubprivate/OEBPS/Images/41662_62_4.jpg?sign=1738833761-z5Aq1tyrkSXcsFPQipUwbOy79xJPkXon-0-38e8d61b71d695e8257d33fea1be5a85)
![](https://epubservercos.yuewen.com/88BA42/21190707608528606/epubprivate/OEBPS/Images/41662_63_1.jpg?sign=1738833761-7vLMX23e4W4XvflUrss9Ip0F7F7fO5bx-0-4869ede2a2a57600f6c10f7e83ef1bcf)
以赋值语句a:=333为例,完成词法解析与语法分析时,此赋值语句将以AssignStmt结构表示。
![](https://epubservercos.yuewen.com/88BA42/21190707608528606/epubprivate/OEBPS/Images/41662_63_2.jpg?sign=1738833761-VHE9Qv4hLdnuGnQFtWAn7QkeANcp9b91-0-ded40b354d0584756a93b585b21bd279)
其中Op代表操作符,在这里是赋值操作OAS。Lhs与Rhs分别代表左右两个表达式,左边代表变量a,右边代表常量333,此时其类型为intLit。
3.3.2 抽象语法树生成与类型检查
完成语法解析后,进入抽象语法树阶段。在该阶段会将词法解析阶段生成的AssignStmt结构解析为一个Node,Node结构体是对抽象语法树中节点的抽象。
![](https://epubservercos.yuewen.com/88BA42/21190707608528606/epubprivate/OEBPS/Images/41662_63_3.jpg?sign=1738833761-PcRS688lOG5d30VgARLkO1qbUJKSwRqP-0-22df7f4ed491e94341c74dead9ec546e)
![](https://epubservercos.yuewen.com/88BA42/21190707608528606/epubprivate/OEBPS/Images/41662_64_1.jpg?sign=1738833761-10jEUXi4iLHZmvqOzYNnZIpoLYtnFDyN-0-98f0cc318db6e54071d0cd8ddebddf16)
其中,Left(左节点)代表左边的变量a,Right(右节点)代表整数333,其Op操作为OLITERAL。Right的E接口字段会存储值333,如果前一阶段为IntLit类型,则需要转换为Mpint类型。Mpint类型用于存储整数常量,具体结构如下所示。
![](https://epubservercos.yuewen.com/88BA42/21190707608528606/epubprivate/OEBPS/Images/41662_64_2.jpg?sign=1738833761-m2z7BtHe4FAkgc52T0FmSnrjFuUIYDJL-0-98357f40a474d1c131dc05852bbc117f)
从Mpint类型的结构可以看到,在编译时AST阶段整数通过math/big.Int进行高精度存储,浮点数通过big.Float进行高精度存储(关于math/big库,详见第2章)。
在类型检查阶段,右节点中的Type字段存储的类型会变为types.Types[TINT]。types.Types是一个数组(var Types [NTYPE]*Type),存储了不同标识对应的Go语言中的实际类型,其中,types.Types[TINT]对应Go语言内置的int类型。
接着完成最终的赋值操作,并将右边常量的类型赋值给左边变量的类型。具体实现位于typecheckas函数中。
![](https://epubservercos.yuewen.com/88BA42/21190707608528606/epubprivate/OEBPS/Images/41662_64_3.jpg?sign=1738833761-ECYjlWvxid3BmdCoVG2uaMb6HEjco5hP-0-a1c0505f5ddad1a10e0ccdce115a1cc3)
在SSA阶段,变量a中存储的大数类型的333最终会调用big.Int包中的Int64函数并将其转换为int64类型的常量,形如:v4(?)=MOVQconst<int>[333](a[int])。