1 / 37

类型检查

类型检查. 第六章 类 型 检 查. 本章内容 静态检查中最典型的部分 — 类型检查: 类型系统、类型检查、多态函数、重载。 忽略其它的静态检查:控制流检查、唯一性检查 、相关名字检查。. 语义分析. 类型检查 验证程序中执行的每个操作是否遵守语言的类型系统的过程,编译程序必须报告不符合类型系统的信息。 控制流检查 控制流语句必须使控制转移到合法的地方。例如,在 C 语言中 break 语句使控制跳离包括该语句的最小 while 、 for 或 switch 语句。如果不存在包括它的这样的语句,则就报错。 一致性检查

bunme
Download Presentation

类型检查

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 类型检查

  2. 第六章 类 型 检 查 本章内容 • 静态检查中最典型的部分 — 类型检查: 类型系统、类型检查、多态函数、重载。 • 忽略其它的静态检查:控制流检查、唯一性检查 、相关名字检查。

  3. 语义分析 • 类型检查 • 验证程序中执行的每个操作是否遵守语言的类型系统的过程,编译程序必须报告不符合类型系统的信息。 • 控制流检查 • 控制流语句必须使控制转移到合法的地方。例如,在C语言中break语句使控制跳离包括该语句的最小while、for或switch语句。如果不存在包括它的这样的语句,则就报错。 • 一致性检查 • 在很多场合要求对象只能被定义一次。例如Pascal语言规定同一标识符在一个分程序中只能被说明一次,同一case语句的标号不能相同,枚举类型的元素不能重复出现等等。 • 相关名字检查 • 有时,同一名字必须出现两次或多次。例如,Ada语言程序中,循环或程序块可以有一个名字,出现在这些结构的开头和结尾,编译程序必须检查这两个地方用的名字是相同的。 • 名字的作用域分析

  4. 6 类型分析 每个程序设计语言都有自己的类型机制,包括类型说明和使用。 类型分析是编译器语义分析的重要组成部分。编译器首先根据类型说明,确定每一个变量和常量的类型,计算其使用存储空间的大小,从而建立其到存储空间的映射。进而,编译器要确定每个语言结构的类型,以完成下面的主要任务: (1) 判定重载算符(函数)在程序中代表的是哪一个运算;(2) 进行类型转换;(3) 对语言结构进行类型检查。

  5. 6.1 类型表达式 6.1.1 类型表达式定义 语言结构的类型由类型表达式表示,类型表达式依赖于程序语言的类型体制。类型表达式或者是简单类型表达式,或者是构造符作用在类型表达式上得到的类型表达式。类型表达式的定义如下: (1) 类型名和基本类型是类型表达式。integer、char、real、boolean是基本类型,所以它们是类型表达式。另外,void表示“无类型”,type_error表示“出错类型”,它们也是类型表达式。

  6. 6.1 类型表达式 (2)类型构造符作用于类型表达式的结果 仍然是类型表达式。类型构造符包括: (a)数组构造符ARRAY:若T是类型表达 式,则ARRAY(I,T)是类型表达式。 (b)笛卡儿乘积:若T1、T2是类型表达 式,则T1 T2是类型表达式,且是左结合的。 (c)记录类型构造符RECORD:若有标识符N1、N2……、Nn与类型表达式T1、T2、…、Tn, 则RECORD((N1 T1)  (N2 T2)…(Nn Tn))是一个类型表达式,它表示一个记录类型。

  7. 6.1 类型表达式 (d)指针类型构造符POINTER:若T是类型表达式,则POINTER(T)是类型表达式,它表示一个指针类型。 (e)函数类型构造符→:若D1、D2、…、Dn和R是类型表达式,则D1D2…… Dn→R是类型表达式,其中优先于→,它表示从定义域类型D1 D2… Dn到值域类型R的映射。 (3) 类型表达式中可出现类型变量,变量是类型表达式。

  8.  设有Pascal 程序片段: TYPE stype=RECORD               name:ARRAY [1..8] OF char;             score:integer              END;  VAR   table:ARRAY [1..50] OF stype;         p: ↑stype; 则stype代表的类型表达式 RECORD((nameARRAY(1..8,char))  (score  integer)) 和table绑定的类型表达式 ARRAY(1..50,stype) 和p绑定的类型表达式  POINTER(stype)

  9. 例1 设有下面的函数定义 FUNCTION f(P1:T1;P2:T2;…,Pn:Tn):T; BEGIN ……END; 和f绑定的类型表达式 T1T2… Tn→T 例2 在函数式语言中可如下定义恒等函数 fun f(x)=x x可以是任何类型的语言结构。因此x可以是任何类型。f的类型表达式为  为类型变量,其值是任何类型表达式。

  10.  pointer char char integer 类型表达式的表达方法 1.树:内部结点是类型构造符,叶结点是基本类型,类型名或类型变量。例如, FUNCTION f (a,b:char):integer: ……… charcharpointer(integer)

  11. 类型表达式的表达方法 2.位串(对类型表达式作某些限制) 例如,考虑由POINTER、→和ARRAY 作为构造符的类型表达式。pointer(t)表示指向类型为t的指针,freturns(t)表示若干变元的函数,其中t为函数返回对象的类型,而函数变元的类型则存放在其它地方。ARRAY(t) 表示元素类型为t的数组,而数组元素的个数保存在其它地方。这样,构造符都成为一元算符,它们作用于基本类型形成的类型表达式有非常统一的结构。

  12. 类型构造符 编码 pointer 01 array 10 freturns 11 基本 类型 编码 boolean 0000 char 0001 integer 0010 real 0011 类型 表达式 编码 char 000000 0001 freturns(char) 000011 0001 pointer(freturns(char)) 000111 0001 array(pointer(freturns(char))) 100111 0001

  13. 6.1.2 类型表达式的等价 • 根据语言的类型体制和类型表达式的表示方法,分结构等价和名字等价。 结构等价 两个类型表达式结构等价,当且仅当它们完全相同。图6-6是测试两个类型表达式结构等价的算法,假设类型构造符仅有数组、笛卡儿积、指针和函数,这个算法递归地比较两个类型表达式的结构。 FUNCTION eq(s,t):boolean; BEGIN IF s 和 t 是相同的基本类型 • THEN return(true)

  14. ELSE IF (s=ARRAY(s1,s2)) and (t=ARRAY(t1,t2)) THEN return(eq(s1,t1) and eq(s2,t2)) ELSE IF (s=s1s2) and (t=t1t2) THEN return(eq(s1,t1) and (eq(s2,t2)) ELSE IF (s=POINTER(s1)) and (t=POINTER(t1)) THEN return(eq(s1,t1)) ELSE IF (s=s1→s2) and (t=t1→t2) THEN return(eq(s1,t1) and eq(s2,t2)) ELSE return(false) END

  15. 类型表达式中的名字     类型可以命名。例如, TYPE Link=↑cell; VAR next: Link; (6-2) Last: Link; P: ↑cell; q,r: ↑cell; 所谓名字等价,是指两个等价类型有同一个名字,也就是说,两个不同的类型名表示不同的类型。在结构等价中,把类型表达式中的所有名字用它们所代表的类型表达式替换后,两个类型表达式等价即是结构等价。

  16. 例6.2 下面给出和声明(6-2)中的5个变量相 联系的类型表达式。          变 量 类 型表 达 式 next Link last Link p Pointer(cell) q Pointer(cell) r Pointer(cell) 在名字等价下,变量next和last有同样的类 型; next和p的类型不相同。在结构等价下, 所有五个变量都有同样的类型。

  17. 不同的语言中,通过声明将变量标识符和类型相联系的规则是不同的,在解释这些规则时,结构等价和名字等价是两个有用的概念。     例6.3  在一些Pascal的实现中,用隐含的类型名和每个声明的变量标识符相联系,如果说明中出现没有名字的类型表达式,就建立一个隐含的类型名。 如(6-2)中的声明是如下处理的:    TYPE VAR  Link=↑cell; next : link; np=↑cell;  last : link; nqr=↑cell;  p : np; q: nqr; r : nqr;        

  18. 典型的实现是构造一张类型图,每当遇到类 型构造符和基本类型,就建立一个新结点,但 要记住类型名所命名的类型表达式。在这种方 法中,如果两个类型表达式用类型图中同样的 结点表示,那么,它们等价。 next last p q r pointer pointer pointer link = cell 图6-7

  19. 类型表示中的环 链表和树结构经常是递归定义的。它们的结点通常定义成一个记录,记录中含有指向同类型记录的指针。 设链表中的结点含有一个整型信息和一个指向下一个结点的指针,实现链表的类型定义: TYPE link=node; node=RECORD info:integer; next: link END;

  20. 用图表示类型表达式 node=record node=record       info integer info integer pointer next pointer next node a. 无环 b. 有环

  21. cell = record  : : info integer next pointer cell C语言对除记录(结构)以外的所有类型使用结构等价,而记录类型用的是名字等价,以避免类型图中的环。

  22. 6.2 类型分析 6.2.1 变量标识符和类型表达式的绑定 程序说明部分建立计算环境, 其中说明了每个变量标识符以及与之绑定的类型。语法(6-3)是一个简单的程序语言语法,该程序由一系列声明D和随后的一个表达式E组成,假设数组的下标从1开始。  文法G[P],产生式如下:  (6-3)     P→D;E     D→D;D|id:T      T→char| integer| ARRAY[num] OF T|↑T     E→num |id| E MOD E| E[E]| E↑

  23. 语义分析程序首先处理类型说明,建立类型 表达式,然后处理变量说明,建立变量和类型 表达式的绑定。具体实现是把变量标识符的类 型信息记录在其符号表的表项中,过程 addtype(id.entry,T.type)完成这个任务,其 翻译模式由图6-4给出

  24. P→D;E    D→D;D    D→id:T  {addtype(id.enery,T.TYPE)}    T→char  {T.type:=char}    T→integer  {T.type:=integer}    T→↑T1  {T.type:=POINTER(T1.type)}    T→ARRAY[num] OF T1 {T.type:=ARRAY(num.val,T1.type)} 图6-4 建立变量标识符和类型属性绑定 的翻译模式

  25. 6.2.2 类型检查 • 类型检查可以分为动态和静态两种。 • 动态检查在运行时刻完成。功效很低。但是如果语言允许动态确定类型,动态检查是必须的。 • 静态检查在编译时刻完成。静态检查是高效的。 • 如果一个语言能够保证经过静态检查之后,程序没有运行时刻的类型错误,则称为强类型的。 • 类型检查的内容包括: • 表达式 • 语句 • 函数

  26. 表达式的类型检查      检查运算对象之间的类型是否满足相容条件,函数lookup(e)取符号表中保存在条目e中的类型。   E→num {E.type:=integer} E→id {E.type:=lookup(id.entry)} E→E1 MOD E2{E.type:= IF (E1.type=integer)AND(E2.type=integer) THEN integer ELSE type_error} E→E1[E2] {E.type:= IF(E2.type=integer)AND(E1.type=ARRAY(s,t)) THEN t ELSE type_error } E→E1↑ {E.type:=IF E1.type=POINTER(t) THEN t ELSE type_error} 表达式的类型检查的翻译模式

  27. 语句的类型检查 • 语句的类型检查主要包括:赋值语句类型的相容性,控制表达式的结果类型检查。 • 指派给语句的类型是基本类型void,如果在语句中发现类型错误, 指派给语句的类型是type_error。

  28. S→id:=E {S.type:=IF id.type=E.type                          THEN void ELSE type_error}    S→IF E THEN S1 {S.type:=IF E.type=boolean                         THEN S1.type ELSE type_error}   S→WHILE E DO S1 {S.type:=IF E.type=boolean                          THEN S1.type ELSE type_error}   S→S1;S2 {S.type:=IF (S1.type=void)AND(S2.type=void)                     THEN void  ELSE type_error} 图6-5检查语句类型的翻译模式

  29. 函数引用的类型检查     对说明部分的分析,应该能知道被引用函数 的类型。翻译模式为: T→T1‘ →’T2 {T.type:=T1.type→T2.type} 函数引用可看成一个表达式作用于另一个表 达式,它的类型检查是: E→E1(E2) {E.type:=IF (E2.type=s)AND(E1.type=s→t)                    THEN t ELSE type_error } 把单个参数推广到多个参数,类型为 T1、T2、…、Tn的n个变元可以看成类型 为T1T2 …  Tn的一个变元。

  30. 6.2.2 类型转换 • 一般的程序设计语言中都规定了某些类型之间的转换关系:比如说整数量可以被当作实数量参与运算,并且不需要程序员显式说明。 • 不同类型的常数在计算机中有不同的表示。当一个值需要转换成为其它类型使用的时候,需要使用某些代码进行转换。 • 因此,编译程序要识别需要进行类型转换的地方,并相应地生成代码。 • 程序设计语言的设计者需要考虑什么情况下需要和可以进行转换。

  31. 6.2.2 类型转换     考虑表达式x +i,其中x是实型,i是整形,它的 后缀式可能是 x i inttoreal real+ 语言的定义会指出必须要做的类型转换。 例如,当整数赋给实型变量时,应该把赋值号右边对 象转换成左边对象的类型。   例6.6  考虑把算术运算符op作用于常数和标识符形成的表达式,假设运算对象有整型和实型两个类型 ,函数lookup(e)返回符号表中保存在e条目中的类型。

  32. E→num {E.type:=integer}     E→num . num {E.type:=real}     E→id {E.type:=lookup(id.entry)}     E→E1 op E2 {E.type:= IF (E1.type=integer)AND(E2.type=integer)        THEN integer ELSE     IF (E1.type=integer)AND(E2.type=real)        THEN real ELSE     IF (E1.type=real)AND(E2.type=integer)        THEN real  ELSE     IF (E1.type=real)AND(E2.type=real)           THEN real  ELSE type_error }图6-9 需要强制类型转换的类型检查

  33. 运算符(函数)的重载 多态函数 • 重载运算符(overloading operator) • 根据上下文可以执行不同的运算。+是重载符号,在A+B中,当A和B为整数、实数、复数或者矩阵时,运算符执行不同类型的运算。当出现重载运算符时,要确定它所表示的唯一的意义,称为运算符识别。检查运算符的操作数。 • 多态函数 • 能实现对数据结构进行操作的算法,不管数据结构的元素类型是什么。 • 多态函数的特点 • 每次被调用时,传递过来的参数可以具有不同类型。

  34. 作业 6.1 假如有下面的Pascal说明 TYPE atype=ARRAY [0..9,-10..10] OF integer;          cell=RECORD               a,b:integer END;               pcell=↑cell;               foo=ARRAY [1..100] OF cell; FUNCTION bar(r:integer;y:cell):pcell; BEGIN……END; 写出atype,cell,pcell,foo和bar的类型表达式。

  35. 作业 6.2 文法G[P]及其产生式如下: P→D;E D→D;D|id:T T→list of T|char|integer E→(L)|Literal|num|id L→E,L|E 这个文法产生一个表。符号的解释和文法 (6-3)相同,但增加了类型List,它表示类型T的 元素表。写一个类似6.2.1中图6-4的翻译模式,以确定表达式(E)和(L)的类型 (注:表中所有元素的类型都是一样的)。

  36. 作业 6.3 修改图6-5的翻译方案,使之能够处理: a) 语句有值。赋值语句的值是赋值号右边 的表达式的值。条件语句或while语句的值 是语句体的值,语句表的值是表中最后 一个语句的值。 b) 布尔表达式。加上逻辑算符and,or和not, 还有关系算符的产生式,然后给出适当 的翻译规则,计算出这些表达式的类型。

  37. 作业 6.4 假定类型定义如下: TYPE link=cell; cell=RECORD info:integer; next: link END; 下面哪些表达式结构等价?哪些名字等价? (1)Link (2)pointer(cell) (3)pointer(Link)  (4)pointer(record(infointeger) (next  pointer(cell)))

More Related