Verificação de Tipos • Verificação de erros semânticos. • Verificação estática (e não dinâmica) – Estática: em tempo de compilação – Dinâmica: em tempo de execução • Objetivo: detecção de alguns tipos de erros Tipos de Verificações Estáticas • Verificação de tipos: soma de variáveis de tipos incompatíveis, atribuição de expressões incompatíveis com variáveis. • Verificações de fluxo de controle: goto entre procedimentos, break em C sem while/for/switch. • Verificações de unicidade: identificadores, labels. • Verificações ligadas a nomes: mesmo nome indicando início e fim de bloco, em Ada. Verificador de Tipos tokens parser Gerador de Verificador código de tipos intermediário árvore árvore sintática sintática Tarefas do verificador de tipos • Tipo dos operandos são compatíveis com tipo do operador • Só ponteiros são derreferenciados • Só arrays são indexados • Número e tipo de argumentos passados a uma função estão corretos Informações geradas pelo verificador de tipos • Além do uso pelo próprio type checker, várias informações de tipos são mantidas para uso em passos posteriores, inclusive geração de código. • Exemplo: overloading do ‘+’. Overloading, coerção e polimorfismo • Overloading: mesmo símbolo ou nome usado para representar operações diferentes. Exemplo: 2 + 2 vs. 2.0 + 2.0 • Coerção: conversão implícita (ou explícita) de um tipo para outro. Exemplo: 2 + 3.5 • Polimorfismo: corpo (código) de uma mesma função é usado para lidar com tipos difrentes. Sistemas de Tipos • Podem ser definidos formalmente ou informalmente. • Define os tipos válidos e as regras para se atribuir tipos às construções da linguagem. Sistemas de Tipos • 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.” • Manual de Referência do C: “O resultado do operador unário & é um ponteiro para o objeto referido pelo operando. Se o tipo do operando for ‘…’ o tipo do resultado é ‘ponteiro para …’” • Cada expressão tem um tipo associado a ela. Tipos básicos e tipos construídos • Tipos básicos (atômicos): boolean, character, integer, real. • Subrange (1..10) e tipos enumerados (verao,inverno,outono, primavera) podem ser tratados como tipos básicos. • Tipos construidos: arrays, registros, conjuntos (são formados de outros tipos construidos ou básicos) • Ponteiros e funções podem ser vistos como tipos construídos. Expressões de Tipos • Tipos em uma linguagem são expressos por “expressões de tipos”: tipos básicos ou construtores de tipos aplicados a outras expressões de tipos. • Os construtores possíveis variam de acordo com a linguagem. Tipos em uma Linguagem • Tipos básicos: boolean, integer, char, real, type_error, void. • Nome de uma expressão de tipos Tipos em uma Linguagem: Construtores de tipos • Tipo Array: var A: array[1..10] of integer • Tipo Produto: T1 T2 • Tipo Registro: é um tipo produto com nomes: type row = record address: integer; lexeme: array [1..15] of char; end; é o mesmo que record((address integer) (lexeme array(1..15,char)) Construtores de Tipos (cont.) • Tipo Ponteiro: var p: ^row declara uma variável do tipo pointer(row) • Tipo Função: mapeiam valores de um domínio D para um domínio destino R, i.e. D R. Exemplo: a função mod tem tem tipo int int int – Por razões de implementação, às vezes os tipos suportados como resultado de uma função são limitados em algumas linguagens. Tipos podem ser representados por grafos int int int char pointer char int pointer int char Sistemas de Tipos • • • Coleção de regras para atribuir expressões de tipo a várias partes de um programa. Verificação de tipos pode sempre ser feita dinamicamente, se junto com os dados vierem sempre informações de tipos. Um sistema de tipos sound elimina a necessidade de verificações dinâmicas, porque permite detectar estaticamente se erros de tipos podem ocorrer em um programa. Sistemas de Tipos (cont.) • • Uma linguagem é dita fortemente tipada (strongly typed) se ela garante que erros de tipo não podem ocorrer durante a execução. Na pratica, algumas verificações só podem ser feitas dinamicamente. Exemplo: índices de arrays. Especificação de um type checker simples PD;E D D ; D | id : T T char | integer | array [ num ] of T | ^T E literal | num | id | E mod E | E [ E ] | E^ • Exemplo: key : integer; key mod 1999 Especificação de um type checker simples (cont.) PD;E DD;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)} T ^T1 {T.type = pointer(T1.type)} Especificação de um type checker simples (cont.) E literal {E.type = char} E num {E.type = integer} E id {E.type = lookup(id.entry)} E E1 mod E2 {if (E1.type == integer && E2.type == integer) E.type = integer else E.type = type_error } Especificação de um type checker simples (cont.) E E1 [ E2 ] {if (E2.type == integer && E1.type == array(s,t)) E.type = t else E.type = type_error } E E1^ {E.type = if (E1.type == pointer(t)) t else type_error } Verificação de tipos de comandos (statements) S id = E {S.type = if (id.type == E.type) void else type_error } S if E then S1 {S.type = if (E.type == boolean) S1 .type else type_error } Verificação de tipos de comandos (statements) (cont.) S while E do S1 {S.type = if (E.type == boolean) S1.type else type_error } S S1 ; S2 {S.type = if (S1.type == void && S2.type == void) void else type_error } Verificação de funções T T1 T2 {T.type = T1.type T2.type} E E1 ( E2 ) {E.type = if (E2.type == s && E1 .type == s t) t else type_error } Equivalência de tipos • • Geralmente é usada equivalência estrutural. Às vezes é necessário fazer modificações, por exemplo: – – – Em várias linguagens, os índices de arrays em um tipo são ignorados em passagem de parâmetros. Tratamento de nomes de tipos e tipos sinônimos (equivalência estrutural x equivalência por nomes). A função de equivalência deve tratar tipos recursivos! Ciclos na representação de tipos type link = ^cell; cell = record info : integer; next : link; end; info cell = record integer next pointer Ciclos na representação de tipos (cont.) • C evita ciclos na representação de tipos usando equivalência estrutural para todos os tipos exceto records (structs). Ciclos na representação de tipos (cont.) • struct cell { int info; struct cell *next; }; cell = record info integer next pointer Cell