IF688 – Checagem de tipos
Resumo desta aula
• Sistema de tipos em um compilador
• Conceitos
– Expressões de tipo
– Sistema de tipo: definição com gramática de atributos e implementação usando visitors
– Conversão de tipos (coerção e casting), tempo de checagem (estática e dinâmica), rigor da
checagem (forte e fraca), polimorfismo (ad-hoc, subtipo, e paramétrico)
Sistema de tipos
• O que faz?
– Identifica certos tipos de erros estaticamente
• Para que serve?
– Facilitar programação
– Evitar escapamento de erros simples – etc.
Sistema de tipos
• O que faz?
– Identifica certos tipos de erros estaticamente
• Para que serve?
– Facilitar programação
– Evitar liberação de erros simples – etc.
O problema de encontrar erros é
indecidível, no caso geral.
Em tempo de compilação
Exemplos de erro (ou warning)
• Tipos incompatíveis: soma, atribuição, chamada de método, etc.
• Fluxo de controle: break ou continue fora de loop ou switch
• Definição de variável sem uso (warning)
• ...
Exemplos de erro (ou warning)
• ...
• Uso de variável sem definição
• Indexação em variável não array
• De-referência em variável não ponteiro
• etc.
Organização do compilador
parser Verificador de tipos
tokens
Gerador de código intermediário
árvore sintática
tabela de símbolos
árvore sintática
Sistema de tipos
• Define os tipos válidos e as regras para se atribuir tipos às construções da linguagem
• Podem ser definidos formalmente ou informalmente
Opção para esta disciplina.
Exemplo Pascal
• Report do Pascal
– “Se os dois operandos dos operadores aritméticos de adição, subtração e
multiplicação são do tipo inteiro, então o resultado é do tipo inteiro.”
Exemplo C
• Manual de Referência de C
– “O resultado do operador unário & é um
ponteiro para o obj. referido pelo operando.
Se o tipo do operando for T o tipo do resultado é ponteiro para T”
Cada expressão tem
um tipo associado!
Tipos básicos e derivados
• Básicos: inteiro, char, string, tipos
enumerados, subrange (exemplo 1..10), etc.
• Derivados: ponteiro para inteiro, array de char, registro, classe, etc.
Expressões de tipo
• Construções da linguagem que declaram tipos
– Construtores básicos. Eg., integer, boolean, etc.
– Construtores elaborados: E.g., record, class, etc.
Construtores elaborados referem-se a outros tipos em uma cadeia com tipos
básicos no final. Não confundir com tipo
de uma expressão
Construtores de tipos
• Array: array[1..10] of integer
• Tipo Registro:
record
address: integer;
lexeme: array [1..15] of char;
end;
• Ponteiro: ^row
• Produto: T1 T2
• Função: integer integer integer
Tipos de variáveis
var ar: array[1..10] of integer;
Expressão de tipo.
Sistema de tipos
• Coleção de regras que definem o tipo associado às várias partes do programa
– Violação de regra => erro de tipo
Exemplo
• Considerando que
– Função foo possui tipo real boolean – Variável x possui tipo inteiro
• Erros de tipo:
foo(1.0) +
!foo(x + 1.0) x
foo(x, 1.0)
Type checker simples
P D ; E
D D ; D | id : T
T char | integer | array [ num ] of T | ^T E literal | num | id | E mod E | E [ E ] | E^
key : integer;
key mod 1999
Exemplo
Type checker simples
P D ; E D D ; D
D id : T {addtype(id.entry, T.type)}
T char {T.type = char}
T integer {T.type = integer}
T array [ num ] of T
{T.type = array(1..num.val, T1.type)}
Type checker simples
T ^T
{T.type = pointer(T1.type)}
E literal {E.type = char}
E num {E.type = integer}
E id {E.type = lookup(id.entry)}
E E mod E
{E.type = if (E1.type == integer &&
E2.type == integer) integer else type_error }
Type checker simples
E E [ E ]
{E.type = if (E2.type == integer &&
E1.type == array(s,t)) = t else type_error }
E E ^
{E.type = if (E1.type == pointer(t)) t else type_error }
Regras Semânticas e Visitors
• Definição dirigida por sintaxe é traduzida em visitors sobre a estrutura do programa
Frequentemene o ambiente de tradução não é flexível o suficiente para definir
sistemas de tipos que usam construções com estas. Por exemplo, if dentro de
regras semânticas.
Exercício 1
• Escreva um visitor para fazer a checagem de tipos conforme definição anterior
Exercício 2
• Adicione regras semânticas para comandos S id = E
S if E then S S while E do S S S ; S
Extensão da linguagem
Resposta
S id = E
{S.type = if (lookup(id.entry) == E.type) void else type_error }
S if E then S
{S.type = if (E.type == boolean) S1 .type else type_error }
Resposta
S while E do S
{S.type = if (E.type == boolean) S1.type else type_error }
S S ; S
{S.type = if (S1.type == void &&
S2.type == void) void else type_error }
Exercício 3
• Modifique a gramática. Adicione tipo função e nova expressão para chamada de função
(considere apenas 1 argumento)
Resposta
T T T E E ( E )
Exercício 4
• Adicione regras semânticas às novas produções
para realizar a checagem de tipos
Resposta
T T T
{T.type = T1.type T2.type}
E E ( E )
{E.type = if (E2.type == s &&
E1 .type == s t) t else type_error }
Conversão de tipo
• Coerção: O compilador adiciona
implicitamente função de conversão
– Normalmente não há perda de informação. Por exemplo, de int para float
• Casting: O programador precisa explicitar
void foo(double d) { …((int) k) * 10…
}
void interpret(Instruction insn) { switch (insn.opcode()) {
case Opcode.IADD :
execArith((ArithInstruction) insn));
break;
…}
Tempo de checagem
• Terminologia: “[Dynam|Stat]ically-typed language”
• Statically-typed: checagem durante compilação
• Dynamically-typed: erros de tipo são checados dinamicamente
Motivo de grande debate até hoje!
Dynamically-typed mais flexível (menos proibitivo) no uso de tipos, porém pode deixar escapar erros e checagem de tipos pode ser custosa.
Rigor do sistema de tipos
• Strong vs. Weak Typing
• Linguagens “strongly-typed” não deixam
escapar (maioria dos) erros de tipo p/ execução Diferença está na flexibilidade no uso de tipos. C permite fazer casting de tipos não relacionados.
Na prática, algumas verificações só podem ser feitas dinamicamente. E.g., índices de arrays.
Exemplos
• Dynamically-typed: Perl, PHP, JavaScript
• Statically-typed: C, Java, C#
• Strongly-typed: Java
• Weakly-typed: C
Polimorfismo Ad-hoc (sobrecarga)
• Mesmo nome de função (ou operador) é usado em contextos diferentes
• Exemplos
– Java: operador + para strings, inteiros, reais – Ada: operador “()” usado para indexar arrays,
chamada de funções, e conversão de tipos
Modificação da gramática
E E1 { E.types = E1 .types }
E id { E.types = lookup(id.entry) } E E (E) { E.types =
{ t | s E2 .types
st E1.types}}
Atributo armazena conjunto de tipos associados a um nó.
Polimorfismo de subtipo
• Qualquer subtipo de T pode ser usado no contexto onde objeto de tipo T é esperado
• E.g., Java
void foo(T t) {…}
Pode-se chamar foo passando qualquer
subtipo de T como parâmetro.
Polimorfismo paramétrico
• Facilita definição de funções que manipulam objetos com estruturas diferentes
• E.g. definição da função length para listas Está para linguagens funcionais assim como polimorfismo de subtipo está para OO.
OO dá suporte com generics.
Exemplo positivo em Haskell
length :: [t] -> Int;
length [] = 0
length (a:as) = 1 + length as
t é uma variável de tipo.
Neste caso, qualquer lista é aceita como
parâmetro.
Exemplo negativo em Pascal
type link = ^cell;
cell = record info: integer; next:
link; end;
function length (lptr : link) : integer;
var len : integer;
begin len = 0;
while (lptr <> nil) do begin
len := len + 1;
lptr := lptr^.next;
end;
length := len;
end;
Resumo desta aula
• Sistema de tipos em um compilador
• Conceitos
– Expressões de tipo
– Sistema de tipo: definição com gramática de atributos e implementação usando visitors
– Conversão de tipos (coerção e casting), tempo de checagem (estática e dinâmica), rigor da
checagem (forte e fraca), polimorfismo (ad-hoc, subtipo, e paramétrico)