38

Compiladores (CC3001) Aula 9: Análise de tipos

  • Upload
    others

  • View
    8

  • Download
    0

Embed Size (px)

Citation preview

Page 1: Compiladores (CC3001) Aula 9: Análise de tipos

Compiladores (CC3001)Aula 9: Análise de tipos

Pedro Vasconcelos

DCC/FCUP

2020

Page 2: Compiladores (CC3001) Aula 9: Análise de tipos

Esta aula

Sistemas de Tipos

Análise de TiposExpressõesDeclarações e comandosFunções

Extras

Page 3: Compiladores (CC3001) Aula 9: Análise de tipos

Re-lembrar: fases dum compilador

texto do programa↓

Análise lexical↓

sequência de tokens

↓Análise sintática

↓árvore sintática abstrata

↓Análise semântica

↓AST & tabela de símbolos

Geração de código↓

código intermédio

Seleção de instruções↓

código assembly simbólico↓

Alocação de registos↓

código assembly concreto↓

Assembler & linker↓

código executável

Page 4: Compiladores (CC3001) Aula 9: Análise de tipos

Sistemas de Tipos

Um sistema de tipos é um conjunto de regras lógicas da linguagem que todos osprogramas devem respeitar.

Exemplos de regras:

I +, -, *, / só operam sobre números

I a condição de if deve ser um boleano

I numa atribuição var = expr a variável deve ser declarada com um tipocompatível com o da a expressão

Page 5: Compiladores (CC3001) Aula 9: Análise de tipos

Diferentes Sistemas de Tipos

Podemos classi�car em dois eixos separados:

Estáticos veri�cação ocorre antes da execução do programa

vs.

Dinâmicos veri�cação ocorre durante a execução do programa

Fortes operações com erros de tipos são proibidas

vs.

Fracos operações com erros de tipos são permitidas

Page 6: Compiladores (CC3001) Aula 9: Análise de tipos

Diferentes Sistemas de Tipos (cont.)

Exemplos:

I Haskell, SML, Java: tipos estáticos e fortes

I Python, Scheme: tipos dinâmicos e fortes

I C: tipos estáticos e fracos

I Estas classi�cações são graduais e não absolutase.g. o sistema de tipos de Java é mais forte que o de C mas mais fraco que o de Haskell

I A distinção estático/dinâmico deixa de fazer sentido para código máquina

Page 7: Compiladores (CC3001) Aula 9: Análise de tipos

Diferentes Sistemas de Tipos (cont.)

(Imagem: Introduction to Compiler Design, Torben Mogensen.)

Page 8: Compiladores (CC3001) Aula 9: Análise de tipos

Porquê análise de tipos?

I Efetuar a análise de tipos durante a compilação (estática):I evita veri�cações durante a execução do programaI permite gerar código mais e�ciente

I Em qualquer caso: ajudam o programador a detetar erros lógicos nos programas

Page 9: Compiladores (CC3001) Aula 9: Análise de tipos

Atributos

I A análise de tipos pode ser feita como uma (ou mais) travesia da AST

I Como as ASTs são estruturas recursivas, a travesia será implementada comofunções recursivas

I As traversias constroem atributos; exemplos:I o tipo de cada sub-expressão;I a tabela que associa cada identi�cador ao seu tipo (ambiente)

I Atributos sintetizados: calculados das folhas da AST para a raiz

I Atributos herdados: passados da raiz da AST para as folhas

I Atributos sintetizados numa parte da AST podem ser herdados noutra(e.g. a tabela de símbolos é sintetizada nas declarações e herdada nas expressões)

Page 10: Compiladores (CC3001) Aula 9: Análise de tipos

Expressões

I Uma linguagem com variáveis, expressões aritméticas e lógicas (comparações)

I Dois tipos: int (números inteiros) e bool (valores lógicos)

Exemplos de expressões:

1+2*3

(1+2)<3

1+x*3 (válida se x for int)(1+x)<y (válida se x for int e y for int)

Um erro de tipos (não podemos somar inteiros com valores lógicos):

1+(2<3)

Page 11: Compiladores (CC3001) Aula 9: Análise de tipos

Atributos sintetizados e herdados

Determinamos o tipo da expressão a partir dos tipos das sub-expressões (atributosintetizado).

<

+

1 2

3

boolxatributo sintetizado

Passamos um tabela de símbolos com os tipos da variáveis (atributo herdado).

<

+

1 x

y

atributo herdado{x 7→ int, y 7→ int}y

Page 12: Compiladores (CC3001) Aula 9: Análise de tipos

Implementação

data Expr = Num Int �- 1, 2, 3

| Var Ident �- x, y, z

| Add Expr Expr �- e1 + e2

| LessThan Expr Expr �- e1 < e2

data Type = TyInt | TyBool

type TypeEnv = Map Ident Type �- ambiente (tabela de símbolos)

checkExpr :: TypeEnv -> Expr -> Type

�- sintetizar o tipo de uma expressão

�- argumentos: o ambiente (TypeEnv) e a expressão (Expr)

�- resultado: é um tipo (ou erro)

�- a definição é por análise de casos

Page 13: Compiladores (CC3001) Aula 9: Análise de tipos

Atributos sintetizados vs. herdados

O tipo da expressão é propagado das folhas para a raiz (atributo sintetizado).

checkExp env (Num n) = TyInt...

checkExpr env (Add e1 e2)

= let t1 = checkExpr env e1

t2 = checkExpr env e2

in if t1==TyInt && t2==TyInt then TyInt

else error "type error in +"

Page 14: Compiladores (CC3001) Aula 9: Análise de tipos

Atributos sintetizados vs. herdados (cont.)

O ambiente (tabela de símbolos) é propagado da raiz para as folhas (atributo herdado).

checkExpr env (Add e1 e2)

= let t1 = checkExpr env e1

t2 = checkExpr env e2

in if t1==TyInt && t2==TyInt then TyInt

else error "type error in +"

Page 15: Compiladores (CC3001) Aula 9: Análise de tipos

Atributos sintetizados vs. herdados (cont.)

O ambiente será determinada na chamada de topo (raiz da AST):

> checkExpr (Map.fromList [("x",TyInt)]) (Add (Var "x") (Num 1))

TyInt

Esta expressão teria um erro de tipo num outro ambiente:

> checkExp (Map.fromList [("x", TyBool)] (Add (Var "x") (Num 1)))

type error in +

(Ver demonstração.)

Page 16: Compiladores (CC3001) Aula 9: Análise de tipos

De�nição completa

checkExpr env (Num n) = TyInt

checkExpr env (Var x) = case Map.lookup x env of

Nothing -> error "undeclared var"

Just t -> t

checkExpr env (Add e1 e2)

= let t1 = checkExpr env e1

t2 = checkExpr env e2

in if t1==TyInt && t2==TyInt then TyInt

else error "type error in +"

checkExpr env (LessThan e1 e2)

= let t1 = checkExpr env e1

t2 = checkExpr env e2

in if t1==TyInt && t2==TyInt then TyBool

else error "type error in <"

Page 17: Compiladores (CC3001) Aula 9: Análise de tipos

Declarações e comandos

I Vamos acrescentar de�nições para declações de variáveis e comandos

I Acrescentamos novos de�nições na sintaxe abstrata e funções para fazer a análisede tipos

Page 18: Compiladores (CC3001) Aula 9: Análise de tipos

Declarações e comandos (cont.)

data Stm = Assign Ident Expr �- var := expr

| If Expr Stm Stm �- if expr then stm1 else stm2

| While Expr Stm �- ciclo while

| Block [Decl] [Stm] �- bloco: declarações e comandos

| Skip �- comando vazio

deriving Show

type Decl = (Ident,Type) �- (ident,tipo)

Page 19: Compiladores (CC3001) Aula 9: Análise de tipos

Exemplos

if (x<y) x=y; else x=0;

If (LessThan (Var "x") (Var "y"))

(Assign "x" (Var "y"))

(Assign "x" (Num 0))

Page 20: Compiladores (CC3001) Aula 9: Análise de tipos

Exemplos (cont.)

n = 1;

while(n < 10)

n = n+1;

Block []

[ Assign "n" (Num 1)

, While (LessThan (Var "n") (Num 10))

(Assign "n" (Add (Var "n") (Num 1)))

]

Page 21: Compiladores (CC3001) Aula 9: Análise de tipos

Exemplos (cont.)

if (x<y) {

int temp;

temp = x;

x = y;

y = temp;

}

If (LessThan (Var "x") (Var "y"))

(Block [("temp",TyInt)]

[ Assign "temp" (Var "x")

, Assign "x" (Var "y")

, Assign "y" (Var "temp")

])

Skip

Page 22: Compiladores (CC3001) Aula 9: Análise de tipos

Regras de tipos para comandos

Atribuição (var := expr) a variável e a expressão devem ter o mesmo tipo

Condição (if exp then stm1 else stm2)

1. a expressão deve ter tipo bool

2. os dois comandos devem estar corretamente tipados

Ciclo (while exp stm)

1. a expressão deve ter tipo bool

2. o corpo deve estar corretamente tipado

Bloco extendemos o ambiente com declarações e veri�camos se todos oscomandos estão bem tipados

Skip está sempre bem tipado

Page 23: Compiladores (CC3001) Aula 9: Análise de tipos

Análise de tipos para comandos

checkStm :: TypeEnv -> Stm -> Bool

I Tal como para expressões vamos passar um ambiente como atributo herdado

I O resultado de analizar dum comando será um boleanoI True se o comando respeita as regras de tiposI ou lança um erro1 com uma mensagem apropriada

I Nos blocos: necessitamos de extender o ambiente

(Ver demonstração.)

1Em vez de False.

Page 24: Compiladores (CC3001) Aula 9: Análise de tipos

Analisar if/else

checkStm env (If cond stm1 stm2)

= let t0 = checkExpr env cond

in if t0 == TyBool then checkStm env stm1 && checkStm env stm2

else error "type error: condition must be bool"

Page 25: Compiladores (CC3001) Aula 9: Análise de tipos

Analisar blocos

checkStm env (Block decls stms)

= let env' = extendEnv env decls

in checkAll env' stms

-- funções auxiliares: extender um ambiente

extendEnv :: TypeEnv -> [Decl] -> TypeEnv

extendEnv env [] = env

extendEnv env ((v,t):rest) = extendEnv (Map.insert v t env) rest

-- verificar tipos de uma lista de comandos

checkAll env [] = True

checkAll env (first:rest) = checkStm env first && checkAll env rest

Page 26: Compiladores (CC3001) Aula 9: Análise de tipos

Análise de tipos para funções

I Vamos extender a linguagem com declarações e chamada de funções

I Simpli�cação: de�nições com uma só expressão (e não comandos)

I Um programa pode ter:I declarações de uma ou mais funções;I seguindo de um comando (pode ser um bloco).

Page 27: Compiladores (CC3001) Aula 9: Análise de tipos

Análise de tipos para funções (cont.)

data Expr = ...

| FunCall Ident [Expr]

�- chamada f(e1, e2, ...)

data FunDef = FunDef Ident Type [(Ident,Type)] Expr

�- t f(x1:t1,..., xn:tn) = expr

data Prog = Prog [FunDef] Stm

�- programa completo: declarações de funções e um comando (�main�)

Page 28: Compiladores (CC3001) Aula 9: Análise de tipos

Exemplo

int incr(int x) { return x+1; }

int main() {

int x, y;

x = 2;

y = incr(x+1);

}

Prog

[FunDef "incr" TyInt [("x",TyInt)] (Add (Var "x") (Num 1))]

[Block [("x", TyInt), ("y",TyInt)]

[ Assign "x" (Num 2)

, Assign "y" (FunCall "incr" [Add (Var "x") (Num 1)])

]

]

Page 29: Compiladores (CC3001) Aula 9: Análise de tipos

Tipos de funções

(t1, t2, . . . , tn) → r

I t1, t2, . . . , tn são os tipos dos argumentos

I r é o tipo do resultado

data Type = ...

| TyFun [Type] Type

Page 30: Compiladores (CC3001) Aula 9: Análise de tipos

Veri�cação de chamadas

Para veri�car uma chamada f (e1, . . . , en)

1. Procurar f no ambiente e obter um tipo (t1, . . . , tn) → r

2. Recursivamente veri�car tipos de e1, . . . , en

3. Se os tipos são iguais a t1, . . . , tn então retornar o tipo do resultado (r); casocontrário erro

Page 31: Compiladores (CC3001) Aula 9: Análise de tipos

Veri�cação de chamadas (cont.)

Uma nova equação na função checkExpr:

checkExpr env (FunCall f args)

= case Map.lookup f env of

Just (TyFun argTypes result) ->

if map (checkExpr env) args == argTypes then result

else error "invalid argument types"

_ -> error "invalid function name"

Page 32: Compiladores (CC3001) Aula 9: Análise de tipos

Veri�cação de de�nições

Para veri�car f (x1 : t1, . . . , xn : tn) : r = e

1. Extendemos o ambiente com {x1 7→ t1, . . . , xn 7→ tn}2. No ambiente extendido: veri�camos o tipo de e

3. Se o tipo resultante for igual a r retornamos um novo ambiente com

{f 7→ (t1, . . . , tn) → r}

Caso contrário: erro.

(Neste caso: o ambiente é simultaneamente herdado e sintetizado.)

Page 33: Compiladores (CC3001) Aula 9: Análise de tipos

Veri�cação de de�nições (cont.)

checkFunDef :: TypeEnv -> FunDef -> TypeEnv

checkFunDef env (FunDef f decls result expr)

= let env' = extendEnv decls env

texpr = checkExpr env' expr

in if texpr == result then

let args = map snd decls

in extendEnv [("f",TyFun args result)] env

else error "wrong result type"

-- função auxiliar (já era usada para blocos)

extendEnv :: TypeEnv -> [Decl] -> TypeEnv

extendEnv env [] = env

extendEnv env ((v,t):rest) = extendEnv (Map.insert v t env) rest

Page 34: Compiladores (CC3001) Aula 9: Análise de tipos

Recursão

I A veri�cação de funções não permite usos recursivos da função, e.g.

int f(int x) = if x > 0 then x*f(x-1) else 1

I A extensão para recursão é simples: basta acresentar uma entrada para f aoambiente usado para tipar o corpo da função2

I Mas só seria útil combinada com a extensão para comandos ou expressões if/else(caso contrário não há forma de terminar a recursão. . . )

2Mais informação em Fundamentos de Linguagens.

Page 35: Compiladores (CC3001) Aula 9: Análise de tipos

Overloading e coerção

Overloading utilizar o mesmo operador para operações em tipos diferentes (e.g. + eminteiros e fracionários)

1 + 2 -- int

1.0 + 2.5 -- float

Coercão conversão implícita ou explícita de tipos

int x = ...;

(float)x + 2.5 -- conversão explícita

x + 2.5 -- conversão implícita

Page 36: Compiladores (CC3001) Aula 9: Análise de tipos

Overloading e coerção (cont.)

I A veri�cação para tipos e operadores pré-de�nidos é simples (e.g. C, Pascal)

I Consideravelmente mais complexa se o programador pode de�nir operadores sobretipos novos (e.g. C++, Haskell,. . . )

Page 37: Compiladores (CC3001) Aula 9: Análise de tipos

Polimor�smo

polimór�co (adjetivo): que se apresenta sob diversas formas.Dicionário Online Priberam

Em programação: a possibilidade de escrever código que opera sobre diferentes tipos.

polimor�smo paramétrico SML/Haskell ou �generics� em Java/C#

reverse :: [t] -> [t] -- em Haskell

void reverse(List<T> list); // em Java

polimor�smo ad-hoc overloading, interfaces, herança. . .

(Tópicos abordados em Fundamentos de Linguagens e Tópicos de Programação Funcional

Avançada.)

Page 38: Compiladores (CC3001) Aula 9: Análise de tipos

Inferência

I Para alguns sistemas é possível inferir os tipos automaticamente em vez de apenasveri�car declarações

I As linguagens funcionais fortemente tipadas usam inferência baseada no algoritmoDamas-Milner (com extensões)

(Tópicos abordados em Fundamentos de Linguagens)