ESC/Java2
© 2003,2004,2005 David Cok and Joseph Kiniry
© 2005 UCD Dublin
© 2003,2004 Radboud University Nijmegen
© 1999,2000 Compaq Computer Corporation
© 1997,1998,1999 Digital Equipment Corporation
All Rights Reserved

javafe.tc
Class TypeCheck

java.lang.Object
  extended byjavafe.tc.TypeCheck
Direct Known Subclasses:
TypeCheck

public class TypeCheck
extends java.lang.Object

The TypeCheck class contains methods to disambiguate, resolve, and check type declarations. (Before methods in this class can be called, the TypeSig class must be initialized.)

Overview of checking, resolution, and disambiguation

This description is out of date, particularly with respect to the names of classes.

Checking involves performing the static checks specified by the Java language specification.

Resolution involves connecting symbolic references in the parse tree to objects representing declarations of the referred-to entities. The parser generates a number of nodes -- instances of Identifier, FieldAccess, and MethodDecl -- containing identifiers found in the input plus a decl field which is initially null. Resolution sets these decl fields to point to the declaration referred to by the identifiers. Similarly, TypeName nodes have a sig field which is initially null and which must be resolved to an instance of TypeSig. For example, the name java.lang.String appearing in a type context would be parsed to a TypeName; resolution of this node would point its sig field to the TypeSig object representing Java's standard String type.

Disambiguation deals with "ambiguous names" in Java (see Section Six of the Java language specification, or this document). These are qualified names of the form I1.I2...In that appear in an expression context. For such a name, I1 could be a local variable or a field of this, or some prefix of the name could be the fully-qualified name of a type, as in java.lang.String.concat.

When it encounters an ambiguous name, the parser generates either an AmbiguousVariableAccess or AmbiguousMethodInvocation node depending on the context. These are leaf nodes. In these cases, disambiguation involves replacing these nodes with appropriate VariableAccess or MethodInvocation nodes; these are non-leaf nodes, and in general the replacement might be fairly deep.

As an example of disambiguation, assume the name x.y is parsed as an AmbiguousVariableAccess. Assume further that no local named x is in scope, the current scope is in an instance method for a class that has a field named x. In this case, disambiguation would replace this AmbiguousVariableAccess with:

(FieldAccess (FieldAccess this x) y)
that is, an instance of FieldAccess whose id field was y and whose expr field was another FieldAccess whose id field was x and whose expr field was an instance of ThisExpr.

An alternative design for disambiguation and resolution was considered. In this design, the Name class, the three subclasses of FieldAccess, and the three subclasses of MethodInvocation would be replaced with a new expression class that looked something like:

 class DotExpr extends Expr {
 int tag; // Indicates the kind of dot
 Expr expr;
 Identifier id;
 }
 

When confronted with phrases of the form I1.I2...In, the parser would generate trees of DotExpr nodes all with the same tag, this tag indicating that the meaning of the dot was ambiguous. Disambiguation would involve replacing this ambiguous tag with a tag whose meaning was clear (e.g., a tag that meant "select a type from a package"). Resolution would involve using our generic decoration mechanism to link certain of these nodes with the declarations to which they refer.

The advantages of this approach over the one we selected is that it is more conventional (it's been used, for example, in compilers for the Modula family of languages), it has a simpler class hierarchy, and it does not involve mutating the structure of the parse tree. The primary advantage of our approach is that we capture many more invariants in the type system, leaving less to go wrong at run-time. It is mostly for this reason that we selected it. In addition, our approach takes less space to represent type names, avoids downcasting (which can be costly time-wise), and is more friendly to the "visitor" pattern.

Staging the processing of type declarations

Resolving and checking a type declaration usually involves looking at other declarations to which it refers. Finding, reading, and processing referred-to types makes resolution and checking fairly complicated. As a result, we have decomposed it up into smaller steps. Type declarations move through a number of states as the resolution and checking process procedes. In addition to making the overall processing of type declarations conceptually more manageable, this decomposition has two other benefits:

To support the lazy handling of type declarations, type declarations are represented using two objects: TypeDecls and TypeSigs. TypeDecl objects represents the actual parse tree of a declaration. TypeSig objects refer to TypeDecl objects. Rather than point directly to TypeDecl, most references to type declarations point to TypeSig objects instead. This extra level of indirection allows us to defer parsing of type declarations until the parse tree is really needed.

Details of the states of type declarations are found with documentation of the TypeSig class.

See Also:
TypeSig, Env, TypeDecl, TypeName, FieldAccess

Field Summary
static TypeCheck inst
          A (possibly extended) instance of TypeCheck.
 
Constructor Summary
TypeCheck()
          Creates a instance of TypeCheck, and sets the inst field to this instance.
 
Method Summary
 boolean canAccess(TypeSig from, TypeSig target, int modifiers, ModifierPragmaVec pmodifiers)
          Can a member of type target with modifiers modifiers/pmodifiers be accessed by code located in from?
 void checkTypeDecl(TypeDecl td)
          Moves td into the checked state.
 void checkTypeSig(TypeSig s)
          Moves s into the checked state.
 Set getAllImplementsSet(MethodDecl md)
          Retrieves the set of interface MethodDecls that a given class MethodDecl implements.
 Set getAllOverrides(MethodDecl md)
          Retrieves the set of MethodDecls that a given MethodDecl overrides or hides.
 Stmt getBranchLabel(BranchStmt s)
          Retrieves the Stmt target of a BranchStmt.
 Set getImplementsSet(ClassDecl cd, MethodDecl md)
          Retrieves the set of interface MethodDecls that a given class MethodDecl implements.
 Set getImplementsSet(MethodDecl md)
          Retrieves the set of interface MethodDecls that a given interface MethodDecl implements.
 java.lang.String getName(RoutineDecl r)
          Returns the user-readable name for a RoutineDecl.
 MethodDecl getOverrides(MethodDecl md)
          Retrieves the class MethodDecl that a given class MethodDecl overrides.
 TypeSig getRawSig(TypeName n)
           
 java.lang.String getRoutineName(RoutineDecl r)
          Returns the user-readable simple name for a RoutineDecl.
 TypeSig getSig(TypeDecl d)
          Retrieves the TypeSig associated with a particular TypeDecl.
 TypeSig getSig(TypeName n)
          Retrieves the TypeSig associated with a particular TypeName.
static java.lang.String getSignature(RoutineDecl r)
          Construct a String listing the signature of a RoutineDecl, omitting the return type and throws causes if any.
 Type getType(VarInit e)
          Retrieves the Type of a VarInit.
 FlowInsensitiveChecks makeFlowInsensitiveChecks()
          Called to obtain the algorithm for performing name resolution and type checking.
 
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
 

Field Detail

inst

public static TypeCheck inst
A (possibly extended) instance of TypeCheck.

Constructor Detail

TypeCheck

public TypeCheck()
Creates a instance of TypeCheck, and sets the inst field to this instance. Only one instance should be created. Also initializes PrepTypeDeclaration.

Method Detail

makeFlowInsensitiveChecks

public FlowInsensitiveChecks makeFlowInsensitiveChecks()
Called to obtain the algorithm for performing name resolution and type checking. By default, returns an instance of FlowInsensitiveChecks.


checkTypeSig

public void checkTypeSig(TypeSig s)
Moves s into the checked state. If any of the supertypes of s are not prepped, they are prepped first.


checkTypeDecl

public void checkTypeDecl(TypeDecl td)
Moves td into the checked state. If any of the supertypes of s are not prepped, they are prepped first.


getType

public Type getType(VarInit e)
Retrieves the Type of a VarInit. This type is associated with an expression by the typechecking pass. If the expression does not have an associated type, then Assert.fail is called.


getBranchLabel

public Stmt getBranchLabel(BranchStmt s)
Retrieves the Stmt target of a BranchStmt. This Stmt may be mentioned either explicitly (as in break label;), or implicitly (as in break;) by the BranchStmt. The correct Stmt target is associated with the BranchStmt by the typechecking pass. This type is associated with an expression by the typechecking pass. If the BranchStmt does not have an associated Stmt target, then Assert.fail is called.


getSig

public TypeSig getSig(TypeDecl d)
Retrieves the TypeSig associated with a particular TypeDecl.


getSig

public TypeSig getSig(TypeName n)
Retrieves the TypeSig associated with a particular TypeName. Precondition: n has been resolved.


getRawSig

public TypeSig getRawSig(TypeName n)

getSignature

public static java.lang.String getSignature(RoutineDecl r)
Construct a String listing the signature of a RoutineDecl, omitting the return type and throws causes if any.

All types are fully qualified if r has been name resolved.

Sample output: "(int, javafe.tc.TypeSig, char[])"

Precondition: PrettyPrint.inst, and r non-null.


getName

public java.lang.String getName(RoutineDecl r)
Returns the user-readable name for a RoutineDecl.

Either of the form "method ()" or the form "constructor ()".

All argument types are fully qualified if r has been name resolved. The method/constructor name is not qualified.

Precondition: PrettyPrint.inst, and r non-null.


getRoutineName

public java.lang.String getRoutineName(RoutineDecl r)
Returns the user-readable simple name for a RoutineDecl.

Precondition: r non-null.


canAccess

public boolean canAccess(TypeSig from,
                         TypeSig target,
                         int modifiers,
                         ModifierPragmaVec pmodifiers)
Can a member of type target with modifiers modifiers/pmodifiers be accessed by code located in from?

Note: pmodifiers may be null.


getOverrides

public MethodDecl getOverrides(MethodDecl md)
Retrieves the class MethodDecl that a given class MethodDecl overrides. If there is no overridden MethodDecl in a superclass, then return null. The returned MethodDecl may be abstract. If multiple class MethodDecl's are overridden, it returns the one lowest in the class hierarchy (furthest away from java.lang.Object). This information is generated by the 'Prep' pass.


getImplementsSet

public Set getImplementsSet(ClassDecl cd,
                            MethodDecl md)
Retrieves the set of interface MethodDecls that a given class MethodDecl implements. This information is generated by the 'Prep' pass.


getAllImplementsSet

public Set getAllImplementsSet(MethodDecl md)
Retrieves the set of interface MethodDecls that a given class MethodDecl implements. This information is generated by the 'Prep' pass.


getImplementsSet

public Set getImplementsSet(MethodDecl md)
Retrieves the set of interface MethodDecls that a given interface MethodDecl implements. This information is generated by the 'Prep' pass.


getAllOverrides

public Set getAllOverrides(MethodDecl md)
Retrieves the set of MethodDecls that a given MethodDecl overrides or hides. This information is generated by the 'Prep' pass.


ESC/Java2
© 2003,2004,2005 David Cok and Joseph Kiniry
© 2005 UCD Dublin
© 2003,2004 Radboud University Nijmegen
© 1999,2000 Compaq Computer Corporation
© 1997,1998,1999 Digital Equipment Corporation
All Rights Reserved

The ESC/Java2 Project Homepage