info.fulloo.trygve.parser.Pass2Listener.java Source code

Java tutorial

Introduction

Here is the source code for info.fulloo.trygve.parser.Pass2Listener.java

Source

package info.fulloo.trygve.parser;

/*
 * Trygve IDE 2.0
 *   Copyright (c)2016 James O. Coplien, jcoplien@gmail.com
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License along
 *  with this program; if not, write to the Free Software Foundation, Inc.,
 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 * 
 *  For further information about the trygve project, please contact
 *  Jim Coplien at jcoplien@gmail.com
 * 
 */

import java.util.List;
import java.util.Map;

import org.antlr.v4.runtime.RuleContext;
import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.tree.TerminalNode;

import info.fulloo.trygve.declarations.AccessQualifier;
import info.fulloo.trygve.declarations.ActualArgumentList;
import info.fulloo.trygve.declarations.ActualOrFormalParameterList;
import info.fulloo.trygve.declarations.Declaration;
import info.fulloo.trygve.declarations.Declaration.ClassOrContextDeclaration;
import info.fulloo.trygve.declarations.Declaration.ObjectSubclassDeclaration;
import info.fulloo.trygve.declarations.FormalParameterList;
import info.fulloo.trygve.declarations.Message;
import info.fulloo.trygve.declarations.TemplateInstantiationInfo;
import info.fulloo.trygve.declarations.Type;
import info.fulloo.trygve.declarations.Type.BuiltInType;
import info.fulloo.trygve.declarations.Type.ClassOrContextType;
import info.fulloo.trygve.declarations.Type.InterfaceType;
import info.fulloo.trygve.declarations.Type.RoleType;
import info.fulloo.trygve.declarations.Type.VarargsType;
import info.fulloo.trygve.declarations.TypeDeclaration;
import info.fulloo.trygve.declarations.Declaration.ClassDeclaration;
import info.fulloo.trygve.declarations.Declaration.ContextDeclaration;
import info.fulloo.trygve.declarations.Declaration.ExprAndDeclList;
import info.fulloo.trygve.declarations.Declaration.MethodDeclaration;
import info.fulloo.trygve.declarations.Declaration.MethodSignature;
import info.fulloo.trygve.declarations.Declaration.ObjectDeclaration;
import info.fulloo.trygve.declarations.Declaration.RoleDeclaration;
import info.fulloo.trygve.declarations.Declaration.TemplateDeclaration;
import info.fulloo.trygve.declarations.Type.ArrayType;
import info.fulloo.trygve.declarations.Type.ClassType;
import info.fulloo.trygve.declarations.Type.ContextType;
import info.fulloo.trygve.declarations.Type.ErrorType;
import info.fulloo.trygve.declarations.Type.StagePropType;
import info.fulloo.trygve.declarations.Type.TemplateParameterType;
import info.fulloo.trygve.declarations.Type.TemplateType;
import info.fulloo.trygve.error.ErrorLogger;
import info.fulloo.trygve.error.ErrorLogger.ErrorIncidenceType;
import info.fulloo.trygve.expressions.Expression;
import info.fulloo.trygve.expressions.Expression.ErrorExpression;
import info.fulloo.trygve.expressions.ExpressionStackAPI;
import info.fulloo.trygve.expressions.MethodInvocationEnvironmentClass;
import info.fulloo.trygve.expressions.Expression.ArrayExpression;
import info.fulloo.trygve.expressions.Expression.ArrayIndexExpression;
import info.fulloo.trygve.expressions.Expression.ArrayIndexExpressionUnaryOp;
import info.fulloo.trygve.expressions.Expression.AssignmentExpression;
import info.fulloo.trygve.expressions.Expression.IdentifierExpression;
import info.fulloo.trygve.expressions.Expression.QualifiedIdentifierExpression;
import info.fulloo.trygve.expressions.Expression.IndexExpression;
import info.fulloo.trygve.expressions.Expression.LastIndexExpression;
import info.fulloo.trygve.expressions.Expression.MessageExpression;
import info.fulloo.trygve.expressions.Expression.NullExpression;
import info.fulloo.trygve.expressions.Expression.QualifiedClassMemberExpression;
import info.fulloo.trygve.expressions.Expression.RoleArrayIndexExpression;
import info.fulloo.trygve.expressions.Expression.UnaryopExpressionWithSideEffect;
import info.fulloo.trygve.parser.KantParser.Builtin_type_nameContext;
import info.fulloo.trygve.parser.KantParser.Class_bodyContext;
import info.fulloo.trygve.parser.KantParser.Method_declContext;
import info.fulloo.trygve.parser.KantParser.Method_decl_hookContext;
import info.fulloo.trygve.parser.KantParser.Method_signatureContext;
import info.fulloo.trygve.parser.KantParser.ProgramContext;
import info.fulloo.trygve.parser.KantParser.Role_bodyContext;
import info.fulloo.trygve.parser.KantParser.Role_declContext;
import info.fulloo.trygve.parser.KantParser.Stageprop_bodyContext;
import info.fulloo.trygve.parser.KantParser.Stageprop_declContext;
import info.fulloo.trygve.parser.KantParser.Type_declarationContext;
import info.fulloo.trygve.semantic_analysis.StaticScope;
import info.fulloo.trygve.semantic_analysis.StaticScope.StaticRoleScope;

public class Pass2Listener extends Pass1Listener {
    public Pass2Listener(ParsingData parsingData) {
        super(parsingData);

        currentScope_ = parsingData_.globalScope();
        currentContext_ = null;
    }

    @Override
    protected ClassDeclaration lookupOrCreateNewClassDeclaration(String name, StaticScope newScope,
            ClassDeclaration rawBaseClass, int lineNumber) {
        return currentScope_.lookupClassDeclarationRecursive(name);
    }

    @Override
    protected void createNewTemplateTypeSuitableToPass(TemplateDeclaration newClass, String name,
            StaticScope newScope, ClassType baseType) {
    }

    @Override
    protected void lookupOrCreateRoleDeclaration(final String roleName, final int lineNumber,
            final boolean isRoleArray) {
        // Return value is through currentRole_
        currentRoleOrStageProp_ = currentScope_.lookupRoleOrStagePropDeclarationRecursive(roleName);
        if (null == currentRoleOrStageProp_) {
            assert null != currentRoleOrStageProp_;
        }
    }

    @Override
    protected void lookupOrCreateStagePropDeclaration(final String stagePropName, final int lineNumber,
            final boolean isStagePropArray) {
        // Return value is through currentRole_
        currentRoleOrStageProp_ = currentScope_.lookupRoleOrStagePropDeclarationRecursive(stagePropName);
        if (null == currentRoleOrStageProp_) {
            assert null != currentRoleOrStageProp_;
        }
    }

    @Override
    public void enterMethod_decl(KantParser.Method_declContext ctx) {
        //  : method_decl_hook '{' expr_and_decl_list '}'
        // This is our own pass 2 version

        // Just for reference:
        // 
        //       method_decl_hook : method_signature

        // There will be a (potentially null) method body. Set up the
        // ExprAndDeclList to receive it.
        final ExprAndDeclList newList = new ExprAndDeclList(ctx.getStart().getLine());
        parsingData_.pushExprAndDecl(newList);

        final Method_decl_hookContext declHookContext = ctx.method_decl_hook();
        final Method_signatureContext signatureCtx = declHookContext.method_signature();
        final String methodSelector = signatureCtx.method_name().getText();
        assert null != methodSelector;

        // Let's say that we overload method X in some scope.
        // Every time through this production, for each declaration,
        // it will return the same declaration (let's say, the first
        // one) and the give it a parameter list.... The second and
        // subsequent entries will never be set up. We fix it by
        // looking up the method according to its place in the source.
        // Crude but effective.
        final MethodDeclaration currentMethod = currentScope_
                .lookupMethodDeclarationRecursiveWithLineNumber(methodSelector, ctx.start.getLine());
        assert null != currentMethod;

        final FormalParameterList pl = new FormalParameterList();

        // Give the method a parameter list
        // This overwrites the one that we gave it in
        // Pass 1 and is likely to be better here in Pass 2,
        // and maybe even better in Pass 3
        currentMethod.addParameterList(pl);

        currentScope_ = currentMethod.enclosedScope();
        parsingData_.pushFormalParameterList(pl);
    }

    @Override
    public void exitMethod_decl(KantParser.Method_declContext ctx) {
        //  : method_decl_hook '{' type_and_expr_and_decl_list '}'
        // Declare parameters in the new scope
        // This is definitely a Pass2 thing
        final MethodSignature signature = parsingData_.popMethodSignature();

        MethodDeclaration currentMethod = currentScope_.lookupMethodDeclarationRecursive(signature.name(),
                signature.formalParameterList(), false);
        if (null == currentMethod) {
            // It could be because the signatures don't match. Try patching up the signature
            // of a leftover current method with a jimmied signature. We'll have to fix
            // this someday to support overloading. (But it seems to work now - probably O.K.)
            currentMethod = currentScope_.lookupMethodDeclarationRecursive(signature.name(),
                    parsingData_.currentFormalParameterList(), false);
        }
        if (null != currentMethod) {
            // It *can* be null. We had an example where one of the
            // parameter types was undeclared and...

            // +++++++++++++++++++++++++
            final Type returnType = signature.returnType();
            currentMethod.setReturnType(returnType);
            if (null != returnType) {
                final String returnTypeName = returnType.getText();
                if (null == currentScope_.lookupTypeDeclarationRecursive(returnTypeName)) {
                    errorHook6p2(ErrorIncidenceType.Fatal, ctx.getStart().getLine(), "Return type `",
                            returnTypeName, "' not declared for `", currentMethod.name(), "'.", "");
                } else {
                    currentMethod.setReturnType(returnType);
                }
            } else {
                final StaticScope currentScope = currentMethod.enclosedScope();
                final StaticScope parentScope = currentScope.parentScope();
                final Declaration otherAssociatedDeclaration = parentScope.associatedDeclaration();
                if (otherAssociatedDeclaration instanceof ContextDeclaration) {
                    if (currentMethod.name().equals(otherAssociatedDeclaration.name())) {
                        ; // then O.K. - constructor
                    } else {
                        errorHook5p2(ErrorIncidenceType.Fatal, ctx.getStart().getLine(),
                                "Return type not declared for `", currentMethod.name(), "'.", "");
                    }
                } else if (otherAssociatedDeclaration instanceof ClassDeclaration) {
                    if (currentMethod.name().equals(otherAssociatedDeclaration.name())) {
                        ; // then O.K. - constructor
                    } else {
                        final TemplateDeclaration templateDeclaration = ((ClassDeclaration) otherAssociatedDeclaration)
                                .generatingTemplate();
                        if (null != templateDeclaration) {
                            if (currentMethod.name().equals(templateDeclaration.name())) {
                                // o.k. - constructors on templates don't include parameter names
                            } else {
                                errorHook5p2(ErrorIncidenceType.Fatal, ctx.getStart().getLine(),
                                        "Return type not declared for template method `", currentMethod.name(),
                                        "'.", "");
                            }
                        } else {
                            errorHook5p2(ErrorIncidenceType.Fatal, ctx.getStart().getLine(),
                                    "Return type not declared for class method `", currentMethod.name(), "'.", "");
                        }
                    }
                } else if (otherAssociatedDeclaration instanceof TemplateDeclaration) {
                    if (currentMethod.name().equals(otherAssociatedDeclaration.name())) {
                        ; // then O.K. - constructor
                    } else {
                        errorHook5p2(ErrorIncidenceType.Fatal, ctx.getStart().getLine(),
                                "Return type not declared for template method `", currentMethod.name(), "'.", "");
                    }
                } else {
                    errorHook5p2(ErrorIncidenceType.Fatal, ctx.getStart().getLine(), "Bad declaration of `",
                            currentMethod.name(), "': ", "bad return type?");
                }
            }
            // +++++++++++++++++++++++++

            this.checkMethodAccess(currentMethod, ctx.getStart().getLine());

            final StaticScope parentScope = currentScope_.parentScope();
            currentScope_ = parentScope;

            @SuppressWarnings("unused")
            final FormalParameterList pl = parsingData_.popFormalParameterList(); // hope this is the right place
            final int lastLineNumber = ctx.getStop().getLine();

            final ReturnStatementAudit audit = new ReturnStatementAudit(currentMethod.returnType(),
                    parsingData_.currentExprAndDecl(), lastLineNumber, this);
            assert null != audit; // just so it's used...
            this.setMethodBodyAccordingToPass(currentMethod);
        }
    }

    private void checkMethodAccess(final MethodDeclaration currentMethod, int lineNumber) {
        final AccessQualifier activeAccessQualifier = currentMethod.accessQualifier();
        final StaticScope currentScope = currentMethod.enclosedScope();
        final StaticScope parentScope = currentScope.parentScope();
        final Declaration otherAssociatedDeclaration = parentScope.associatedDeclaration();
        final Type derivedClassReturnType = currentMethod.returnType();

        if (otherAssociatedDeclaration instanceof ClassDeclaration) {
            ClassDeclaration baseClass = ((ClassDeclaration) otherAssociatedDeclaration).baseClassDeclaration();
            while (null != baseClass) {
                final StaticScope baseClassScope = baseClass.enclosedScope();
                final MethodDeclaration baseClassVersionOfMethod = baseClassScope
                        .lookupBaseClassMethodLiskovCompliantTo(currentMethod);
                if (null != baseClassVersionOfMethod) {
                    final AccessQualifier baseClassAccessQualifier = baseClassVersionOfMethod.accessQualifier();
                    if (baseClassAccessQualifier != activeAccessQualifier) {
                        errorHook6p2(ErrorIncidenceType.Fatal, lineNumber, "Derived class declaration of `",
                                currentMethod.signature().getText(),
                                "' must have same access qualifier as that of declaration in base class `",
                                baseClass.name(), "'.", "");
                        break; // don't cascade errors
                    }

                    // Check Liskov
                    final Type baseClassReturnType = baseClassVersionOfMethod.returnType();
                    if (null != baseClassReturnType && null != derivedClassReturnType
                            && baseClassReturnType.isntError() && derivedClassReturnType.isntError()
                            && baseClassReturnType.canBeConvertedFrom(derivedClassReturnType, lineNumber,
                                    this) == false) {
                        errorHook6p2(ErrorIncidenceType.Fatal, lineNumber, "Return type `",
                                derivedClassReturnType.getText(), "' of `" + currentMethod.signature().getText(),
                                "' in class `" + otherAssociatedDeclaration.name(),
                                "' must be no less restrictive than `",
                                baseClassReturnType.getText() + "' in the base class.");
                    }
                }

                // Next
                baseClass = baseClass.baseClassDeclaration();
            }
        }
    }

    protected void setMethodBodyAccordingToPass(MethodDeclaration unused) {
        /* Nothing. Just clean up the data structures. */
        @SuppressWarnings("unused")
        final ExprAndDeclList body = parsingData_.popExprAndDecl();
    }

    @Override
    public void enterRole_decl(KantParser.Role_declContext ctx) {
        // : 'role' JAVA_ID '{' role_body '}'
        // | 'role' JAVA_ID '{' role_body '}' REQUIRES '{' self_methods '}'
        // | access_qualifier 'role' JAVA_ID '{' role_body '}'
        // | access_qualifier 'role' JAVA_ID '{' role_body '}' REQUIRES '{' self_methods '}'
        // | 'role' JAVA_ID '{' '}'
        // | 'role' JAVA_ID '{' '}' REQUIRES '{' self_methods '}'
        // | access_qualifier 'role' JAVA_ID '{ '}'
        // | access_qualifier 'role' JAVA_ID '{ '}' REQUIRES '{' self_methods '}'

        super.enterRole_decl(ctx);
        this.processRequiredDeclarations(ctx.getStart().getLine());
    }

    @Override
    public void exitRole_decl(KantParser.Role_declContext ctx) {
        // : 'role' JAVA_ID '{' role_body '}'
        // | 'role' JAVA_ID '{' role_body '}' REQUIRES '{' self_methods '}'
        // | access_qualifier 'role' JAVA_ID '{' role_body '}'
        // | access_qualifier 'role' JAVA_ID '{' role_body '}' REQUIRES '{' self_methods '}'
        // | 'role' JAVA_ID '{' '}'
        // | 'role' JAVA_ID '{' '}' REQUIRES '{' self_methods '}'
        // | access_qualifier 'role' JAVA_ID '{ '}'
        // | access_qualifier 'role' JAVA_ID '{ '}' REQUIRES '{' self_methods '}'

        this.processDeclareRoleArrayAlias(ctx.getStart().getLine());
        super.exitRole_decl(ctx); // necessary? some of the cleanup seems relevant
    }

    @Override
    public void enterStageprop_decl(KantParser.Stageprop_declContext ctx) {
        // : 'stageprop' JAVA_ID '{' role_body '}'
        // | 'stageprop' JAVA_ID '{' role_body '}' REQUIRES '{' self_methods '}'
        // | access_qualifier 'stageprop' JAVA_ID '{' role_body '}'
        // | access_qualifier 'stageprop' JAVA_ID '{' role_body '}' REQUIRES '{' self_methods '}'
        super.enterStageprop_decl(ctx);
        this.processRequiredDeclarations(ctx.getStart().getLine());
    }

    protected void processRequiredDeclarations(int lineNumber) {
        currentRoleOrStageProp_.processRequiredDeclarations(lineNumber);
    }

    @Override
    public void enterArgument_list(KantParser.Argument_listContext ctx) {
    }

    @Override
    public void exitMessage(KantParser.MessageContext ctx) {
        //  : method_name '(' argument_list ')'
        //  | type_name '(' argument_list ')'

        // Certified Pass 2 version ;-)

        String selectorName = null;
        if (null != ctx.method_name()) {
            selectorName = ctx.method_name().getText();
        } else if (null != ctx.type_name()) {
            final ExpressionStackAPI typeName = parsingData_.popRawExpression();
            selectorName = typeName.name();
        } else {
            assert false;
        }

        final long lineNumber = ctx.getStart().getLine();

        // This is definitely Pass 2 stuff.
        final ActualArgumentList argumentList = parsingData_.popArgumentList();

        // All arguments are evaluated and are pushed onto the stack
        for (int i = 0; i < argumentList.count(); i++) {
            final Object rawArgument = argumentList.argumentAtPosition(i);
            assert null != rawArgument && rawArgument instanceof Expression;
            final Expression argument = (Expression) rawArgument;
            argument.setResultIsConsumed(true);
        }

        final Type enclosingMegaType = Expression.nearestEnclosingMegaTypeOf(currentScope_);
        final Message newMessage = new Message(selectorName, argumentList, lineNumber, enclosingMegaType);
        parsingData_.pushMessage(newMessage);
    }

    @Override
    protected Expression processIndexExpression(final Expression rawArrayBase, final Expression indexExpr,
            final int lineNumber) {
        Expression expression = null;

        // On pass one, types may not yet be set up so we may
        // stumble here (particularly if there is a forward reference
        // to a type). Here on pass 2 we're a bit more anal
        final Type baseType = rawArrayBase.type();
        if (baseType instanceof RoleType) {
            final String roleName = rawArrayBase.name();
            final RoleType roleBaseType = (RoleType) baseType;
            // Look up the actual array. It is in the current scope as a type
            final StaticScope contextScope = roleBaseType.contextDeclaration().type().enclosedScope();
            final RoleDeclaration roleDecl = contextScope.lookupRoleOrStagePropDeclaration(roleName);
            if (null == roleDecl) {
                assert false;
            } else {
                // do something useful

                final Type contextType = Expression.nearestEnclosingMegaTypeOf(roleDecl.enclosedScope());
                final StaticScope nearestMethodScope = Expression.nearestEnclosingMethodScopeAround(currentScope_);
                final Expression currentContext = new IdentifierExpression("current$context", contextType,
                        nearestMethodScope, lineNumber);
                final Expression roleNameInvocation = new QualifiedIdentifierExpression(currentContext, roleName,
                        roleDecl.type());
                expression = new RoleArrayIndexExpression(roleName, roleNameInvocation, indexExpr);
            }
        } else if (baseType instanceof ArrayType) {
            final Type arrayBaseType = rawArrayBase.type();
            assert arrayBaseType instanceof ArrayType;
            final ArrayType arrayType = (ArrayType) arrayBaseType; // instance of ArrayType
            final Type aBaseType = arrayType.baseType(); // like int
            final ArrayExpression arrayBase = new ArrayExpression(rawArrayBase, aBaseType);
            arrayBase.setResultIsConsumed(true);
            expression = new ArrayIndexExpression(arrayBase, indexExpr, lineNumber);
        } else if (baseType instanceof BuiltInType && baseType.name().equals("void")) {
            // Stumbling error
            expression = new ErrorExpression(rawArrayBase);
        } else if (baseType instanceof BuiltInType && baseType.name().equals("Null")) {
            // Stumbling error
            expression = new ErrorExpression(rawArrayBase);
        } else if (baseType instanceof ErrorType) {
            ; // it happens
        } else {
            assert false;
        }
        return expression;
    }

    @Override
    protected void checkExprDeclarationLevel(RuleContext ctxParent, Token ctxGetStart) {
        // Certified Pass 2 version :-)
        RuleContext executionContext = ctxParent;
        while ((executionContext instanceof ProgramContext) == false) {
            if (executionContext instanceof Method_declContext) {
                break;
            } else if (executionContext instanceof Stageprop_bodyContext) {
                errorHook5p2(ErrorIncidenceType.Fatal, ctxGetStart.getLine(),
                        "Expression cannot just appear in stageprop scope: it must be in a method", "", "", "");
                break;
            } else if (executionContext instanceof Role_bodyContext) {
                errorHook5p2(ErrorIncidenceType.Fatal, ctxGetStart.getLine(),
                        "Expression cannot just appear in Role scope: it must be in a method", "", "", "");
                break;
            } else if (executionContext instanceof Stageprop_declContext) {
                errorHook5p2(ErrorIncidenceType.Fatal, ctxGetStart.getLine(),
                        "Expression cannot just appear in stageprop scope: it must be in a method", "", "", "");
                break;
            } else if (executionContext instanceof Role_declContext) {
                errorHook5p2(ErrorIncidenceType.Fatal, ctxGetStart.getLine(),
                        "Expression cannot just appear in Role scope: it must be in a method", "", "", "");
                break;
            } else if (executionContext instanceof Class_bodyContext) {
                errorHook5p2(ErrorIncidenceType.Fatal, ctxGetStart.getLine(),
                        "Expression cannot just appear in Class scope: it must be in a method", "", "", "");
                break;
            } else if (executionContext instanceof Type_declarationContext) {
                errorHook5p2(ErrorIncidenceType.Fatal, ctxGetStart.getLine(),
                        "Expression cannot just appear in a global program element scope: it must be in a method",
                        "", "", "");
                break;
            }
            executionContext = executionContext.parent;
        }
    }

    @Override
    public void binopTypeCheck(final Expression leftExpr, final String operationAsString,
            final Expression rightExpr, final Token ctxGetStart) {
        // Certified Pass 2 version ;-)
        final Type leftExprType = leftExpr.type(), rightExprType = rightExpr.type();
        final Type resultType = leftExprType;

        if (resultType.canBeConvertedFrom(rightExpr.type()) == false && leftExpr.isntError()
                && rightExpr.isntError() && leftExpr.type().isntError() && rightExpr.type().isntError()) {
            errorHook6p2(ErrorIncidenceType.Fatal, ctxGetStart.getLine(), "Invalid operands to `" + "",
                    operationAsString, "' on type ", leftExpr.type().name(), " with operand of type ",
                    rightExpr.type().name());
        }
        final ActualArgumentList argList = new ActualArgumentList();
        argList.addActualArgument(rightExpr);
        final Expression self = new IdentifierExpression("t$his", resultType, resultType.enclosedScope(),
                ctxGetStart.getLine());
        argList.addFirstActualParameter(self);
        final StaticScope enclosedScope = resultType.enclosedScope();

        MethodDeclaration mdecl = enclosedScope.lookupMethodDeclarationWithConversionIgnoringParameter(
                operationAsString, argList, false, /*parameterToIgnore*/ null);
        if (null == mdecl) {
            mdecl = enclosedScope.lookupMethodDeclarationRecursive(operationAsString, argList, false);
            if (null == mdecl) {
                mdecl = enclosedScope.lookupMethodDeclarationIgnoringRoleStuff(operationAsString, argList);
                if (null == mdecl && rightExpr.isntError() && rightExpr.type().isntError()
                        && resultType.isntError()) {
                    errorHook6p2(ErrorIncidenceType.Fatal, ctxGetStart.getLine(), "No such operation `",
                            operationAsString, "' on type `", resultType.name(),
                            "' for argument `" + rightExpr.type().name(), "'.");
                }
            }
        }

        // First, check "overloading"
        final ActualArgumentList argumentList = new ActualArgumentList();
        argumentList.addActualArgument(rightExpr);
        argumentList.addFirstActualParameter(leftExpr);
        final MethodDeclaration checkMethodOverload = leftExprType.enclosedScope()
                .lookupMethodDeclarationWithConversionIgnoringParameter(operationAsString, argumentList, false,
                        /*parameterToIgnore*/ null);

        if (null != checkMethodOverload) {
            ; // then the class overloads the operator. O.K.
        } else if (leftExprType.canBeLhsOfBinaryOperatorForRhsType(operationAsString, rightExprType)
                && rightExprType.canBeRhsOfBinaryOperator(operationAsString)) {
            ; // o.k.
        } else if (rightExpr.isntError() && resultType.isntError() && rightExpr.type().isntError()) {
            errorHook6p2(ErrorIncidenceType.Fatal, ctxGetStart.getLine(), "Operation `", operationAsString,
                    "' cannot be applied to type `", resultType.name(), "' for argument `",
                    rightExpr.type().name() + "'.");
        }
    }

    // ------------------------------------------------------------------------------------------------------- 

    public ExpressionStackAPI exprFromExprDotJavaId(TerminalNode ctxJAVA_ID, Token ctxGetStart) {
        // : expr '.' JAVA_ID
        // Pop the expression for the indicated object and message
        // Certified Pass 2 version ;-)

        Type type = null;
        final ExpressionStackAPI qualifier = parsingData_.popRawExpression();
        Expression expression = null;
        final String javaIdString = ctxJAVA_ID.getText();

        if (qualifier.type().name().equals("Class")) {
            // This is where we handle types like "System" for System.out.print*
            // Now we need to get the actual class of that name
            final Type rawClass = currentScope_.lookupTypeDeclarationRecursive(qualifier.name());
            assert rawClass instanceof ClassType;
            final ClassType theClass = (ClassType) rawClass;

            final ObjectDeclaration odecl = theClass.type().enclosedScope().lookupObjectDeclaration(javaIdString);
            if (odecl.type() != null)
                type = odecl.type();
            expression = new QualifiedClassMemberExpression(theClass, javaIdString, type);
        } else {
            final ObjectDeclaration odecl = qualifier.type().enclosedScope()
                    .lookupObjectDeclarationRecursive(javaIdString);

            if (null == odecl) {
                final MethodDeclaration javaId2 = qualifier.type().enclosedScope()
                        .lookupMethodDeclarationRecursive(javaIdString, null, true);
                if (null == javaId2) {
                    errorHook6p2(ErrorIncidenceType.Fatal, ctxGetStart.getLine(), "Identifier `", javaIdString,
                            "' not declared for object `", qualifier.name(), "'.", "");
                    type = new ErrorType();
                } else {
                    type = javaId2.returnType();
                }
            } else {
                type = odecl.type();
                assert type != null;
            }

            assert qualifier instanceof Expression;
            expression = new QualifiedIdentifierExpression((Expression) qualifier, javaIdString, type);
        }

        assert null != expression;

        return expression;
    }

    @Override
    public void ctorCheck(final Type type, final Message message, final int lineNumber) {
        // Is there a constructor?
        // We're not ready for this until here in Pass 2
        final String className = message.selectorName();
        final ActualArgumentList actualArgumentList = message.argumentList();
        if (null != type) {
            // Under error conditions we sometimes get a null type parameter
            final StaticScope declarationScope = type.enclosedScope();

            // Can be null in error condition
            if (null != declarationScope) {
                final String typeName = type.name();
                MethodDeclaration constructor = declarationScope
                        .lookupMethodDeclarationWithConversionIgnoringParameter(typeName, actualArgumentList, false,
                                /*parameterToIgnore*/ null);
                if (null != actualArgumentList && 1 < actualArgumentList.count()) {
                    // So the "new" message actually had arguments, which means
                    // it's expecting a constructor
                    if (null == constructor) {
                        // See if we're passing in a Role, and the Role "requires" signature
                        // can supply everything needed by the called method. "Superconversion"
                        // uses Role "requires" methods
                        constructor = declarationScope.lookupMethodDeclarationWithSuperConversionIgnoringParameter(
                                typeName, actualArgumentList, false, /*parameterToIgnore*/ null);
                        if (null == constructor) {
                            errorHook5p2(ErrorIncidenceType.Fatal, lineNumber, "No matching constructor on class `",
                                    className, "' for `new' invocation", ".");
                        }
                    }
                }

                if (null != constructor) {
                    final String constructorName = constructor.name();
                    final boolean isAccessible = currentScope_.canAccessDeclarationWithAccessibility(constructor,
                            constructor.accessQualifier(), lineNumber);
                    if (isAccessible == false) {
                        errorHook6p2(ErrorIncidenceType.Fatal, lineNumber, "Cannot access constructor `",
                                constructorName, "' with `", constructor.accessQualifier().asString(),
                                "' access qualifier.", "");
                    }

                    final List<String> nonmatchingMethods = message.validInRunningEnviroment(constructor);
                    if (0 < nonmatchingMethods.size()) {
                        errorHook5p3(ErrorIncidenceType.Fatal, lineNumber, "The parameters to script `",
                                constructorName + message.argumentList().selflessGetText(),
                                "' have scripts that are unavailable outside this Context, ",
                                "though some formal parameters of " + constructorName
                                        + " presume they are available (they are likely Role scripts):");
                        for (final String badMethod : nonmatchingMethods) {
                            errorHook5p3(ErrorIncidenceType.Fatal, lineNumber, "\t", badMethod, "", "");
                        }
                    }
                }
            }
        }
    }

    public void addSelfAccordingToPass(final Type type, final Message message, final StaticScope scope) {
        // Apparently called only for constructor processing.
        // The simple part. Add this.
        final Expression self = new IdentifierExpression("t$his", type, scope, 0);
        message.addActualThisParameter(self);
    }

    private MethodSignature declarationForMessageFromRequiresSectionOfRole(final Message message,
            final RoleDeclaration roleDecl) {
        MethodSignature retval = null;
        final Map<String, List<MethodSignature>> requiresDecls = roleDecl.requiredSelfSignatures();
        for (Map.Entry<String, List<MethodSignature>> entry : requiresDecls.entrySet()) {
            if (entry.getKey().equals(message.selectorName())) {
                final List<MethodSignature> potentialRequiresMethods = entry.getValue();
                for (final MethodSignature potentialRequiresMethod : potentialRequiresMethods) {
                    if (null != potentialRequiresMethod) {
                        final FormalParameterList requiresSignature = potentialRequiresMethod.formalParameterList();
                        final ActualArgumentList callingSignature = message.argumentList();
                        if (requiresSignature.alignsWithUsingConversion(callingSignature)) {
                            retval = potentialRequiresMethod;
                            break;
                        }
                    }
                }
                if (null != retval)
                    break;
            }
        }
        return retval;
    }

    private void contextInvocationCheck(final Type nearestEnclosingMegaType, final Message message,
            final Type objectType, final Type wannabeContextType, final Token ctxGetStart) {
        // Don't allow Role methods directly to invoke
        // Context methods. Look up the method in the enclosing
        // context and make sure it's not there. But first we
        // can say it's O.K. if it's either another method
        // in the Role.
        MethodDeclaration testDecl = nearestEnclosingMegaType.enclosedScope()
                .lookupMethodDeclarationIgnoringParameter(message.selectorName(), message.argumentList(), "this",
                        true);
        if (null == testDecl) {
            // Check in the Requires list
            final RoleType objectTypeAsRoleType = (RoleType) objectType;
            final RoleDeclaration roleDecl = (RoleDeclaration) objectTypeAsRoleType.associatedDeclaration();
            final MethodSignature signatureInRequiresSection = declarationForMessageFromRequiresSectionOfRole(
                    message, roleDecl);
            if (null == signatureInRequiresSection) {
                testDecl = wannabeContextType.enclosedScope().lookupMethodDeclarationIgnoringParameter(
                        message.selectorName(), message.argumentList(), "this", true);
                if (null != testDecl) {
                    final StaticScope currentMethodScope = Expression
                            .nearestEnclosingMethodScopeAround(currentScope_);
                    errorHook5p2(ErrorIncidenceType.Noncompliant, ctxGetStart.getLine(),
                            "NONCOMPLIANT: Enacting enclosed Context script `",
                            message.selectorName() + message.argumentList().selflessGetText(),
                            "' from within `" + nearestEnclosingMegaType.name() + "."
                                    + currentMethodScope.associatedDeclaration().name(),
                            "'.");
                }
            }
        }
    }

    private void otherRolesRequiresInvocationCheck(final Type nearestEnclosingMegaType, final Message message,
            final Type objectType, final Type wannabeContextType, final Token ctxGetStart) {
        // Don't allow Role methods directly to invoke
        // the "requires" methods of other Roles in the same Context.
        // But first we can say it's O.K. if it's within the same Role.
        if (currentRoleOrStageProp_.type().pathName().equals(objectType.pathName())) {
            ; // is within the same Role; it's cool
        } else {
            final MethodDeclaration testDecl = nearestEnclosingMegaType.enclosedScope()
                    .lookupMethodDeclarationIgnoringParameter(message.selectorName(), message.argumentList(),
                            "this", true);
            if (null != testDecl) {
                ; // is a regular Role method  is O.K.
            } else {
                // Check in the Requires list
                final RoleType objectTypeAsRoleType = (RoleType) objectType;
                final RoleDeclaration roleDecl = (RoleDeclaration) objectTypeAsRoleType.associatedDeclaration();
                final MethodSignature signatureInRequiresSection = declarationForMessageFromRequiresSectionOfRole(
                        message, roleDecl);
                if (null != signatureInRequiresSection) {
                    // It's O.K. if the signature has been pulled into the Role interface

                    final MethodSignature publishedSignature = roleDecl
                            .lookupPublishedSignatureDeclaration(signatureInRequiresSection);
                    if (null != publishedSignature) {
                        // Is it public?
                        if (publishedSignature.accessQualifier() != AccessQualifier.PublicAccess) {
                            errorHook6p2(ErrorIncidenceType.Fatal, ctxGetStart.getLine(), "Script `",
                                    message.selectorName(), "' is not public and so is not accessible to `",
                                    nearestEnclosingMegaType.name(), "'.", "");
                        }
                    } else {
                        errorHook5p2(ErrorIncidenceType.Noncompliant, ctxGetStart.getLine(),
                                "NONCOMPLIANT: Trying to enact object script `",
                                message.selectorName() + message.argumentList().selflessGetText(),
                                "' without using the interface of the Role it is playing: `" + roleDecl.name(),
                                "'.");
                    }
                }
            }
        }
    }

    private <ExprType> Expression messageSendGetObject(final Token ctxGetStart, final ExprType ctx_abelianAtom,
            final Builtin_type_nameContext ctx_builtin_typeName) {
        // Pop the expression for the indicated object and message
        final Type nearestEnclosingMegaType = Expression.nearestEnclosingMegaTypeOf(currentScope_);
        final StaticScope nearestMethodScope = Expression.nearestEnclosingMethodScopeAround(currentScope_);

        Expression object = null;
        if (ctx_abelianAtom != null) {
            // Error stumbling check
            if (parsingData_.currentExpressionExists()) {
                if (null == parsingData_.peekExpression()) {
                    // Get rid of the null junk (error stumbling logic)
                    @SuppressWarnings("unused")
                    final Object unused = parsingData_.popRawExpression();

                    // Come in with a suitable substitute
                    object = new ErrorExpression(null);
                } else {
                    object = parsingData_.popExpression();
                }
            } else {
                return new ErrorExpression(null); // get out
            }
        } else if (ctx_builtin_typeName != null) {
            // e.g. String.join
            final String typeName = ctx_builtin_typeName.getText();
            final Type theType = currentScope_.lookupTypeDeclarationRecursive(typeName);
            final Type classType = StaticScope.globalScope().lookupTypeDeclaration("Class");
            object = new IdentifierExpression(theType.name(), classType, classType.enclosedScope().parentScope(),
                    ctxGetStart.getLine());
        } else if (null != nearestEnclosingMegaType) {
            object = new IdentifierExpression("this", nearestEnclosingMegaType, nearestMethodScope,
                    ctxGetStart.getLine());
        } else {
            object = new ErrorExpression(null);
        }
        object.setResultIsConsumed(true);

        return object;
    }

    private Expression messageSendGenerateCall(final Token ctxGetStart, final Expression object,
            final MethodDeclaration methodDeclaration, final Message message, final Type returnType,
            final MethodSignature methodSignature) {
        Expression retval = null;
        if (null != methodSignature) {
            MethodInvocationEnvironmentClass originMethodClass = MethodInvocationEnvironmentClass.Unknown;

            if (null != currentScope_.associatedDeclaration()) {
                originMethodClass = currentScope_.methodInvocationEnvironmentClass();
            } else {
                final Type anotherType = object.enclosingMegaType();
                if (null != anotherType) {
                    final StaticScope anotherScope = anotherType.enclosedScope();
                    originMethodClass = anotherScope.methodInvocationEnvironmentClass();
                } else {
                    originMethodClass = MethodInvocationEnvironmentClass.ClassEnvironment; // outermost scope
                }
            }

            MethodInvocationEnvironmentClass targetMethodClass = MethodInvocationEnvironmentClass.Unknown;
            if (null != object && null != object.type()) {
                if (object.type() instanceof StagePropType) {
                    targetMethodClass = MethodInvocationEnvironmentClass.RoleEnvironment;
                } else if (object.type() instanceof RoleType) {
                    targetMethodClass = MethodInvocationEnvironmentClass.RoleEnvironment;
                } else if (object.type() instanceof ContextType) {
                    targetMethodClass = MethodInvocationEnvironmentClass.ContextEnvironment;
                } else if (object.type() instanceof InterfaceType) {
                    // Interfaces wrap classes
                    targetMethodClass = MethodInvocationEnvironmentClass.ClassEnvironment;
                } else if (object.type() instanceof ArrayType) {
                    // Arrays kind of behave like classes, certainly as regards dispatching
                    targetMethodClass = MethodInvocationEnvironmentClass.ClassEnvironment;
                } else if (null != methodDeclaration) {
                    targetMethodClass = methodDeclaration.enclosingScope().methodInvocationEnvironmentClass();
                } else {
                    targetMethodClass = MethodInvocationEnvironmentClass.Unknown;
                }

                // Double-check to make sure that, if this is going through a Role
                // interface, whether it is actually a "requires" declaration
                if (MethodInvocationEnvironmentClass.RoleEnvironment == targetMethodClass) {
                    final RoleType roleType = (RoleType) object.type();
                    final RoleDeclaration roleDecl = (RoleDeclaration) roleType.associatedDeclaration();
                    final MethodSignature requiredSignatureDecl = roleDecl
                            .lookupRequiredMethodSignatureDeclaration(message.selectorName());
                    if (null != requiredSignatureDecl) {
                        // It could be a context, but we can't tell here. We must wait until
                        // run-time and adjust
                        targetMethodClass = MethodInvocationEnvironmentClass.ClassEnvironment;
                    }
                }
            } else if (null != methodDeclaration) {
                targetMethodClass = methodDeclaration.enclosingScope().methodInvocationEnvironmentClass();
            } else {
                targetMethodClass = MethodInvocationEnvironmentClass.Unknown;
            }

            checkForMessageSendViolatingConstness(methodSignature, ctxGetStart);

            boolean isPolymorphic = true;
            if (amInConstructor()) {
                if (object instanceof IdentifierExpression) {
                    if (((IdentifierExpression) object).name().equals("this")) {
                        isPolymorphic = false;
                    }
                }
            }
            retval = new MessageExpression(object, message, returnType, ctxGetStart.getLine(),
                    methodSignature.isStatic(), originMethodClass, targetMethodClass, isPolymorphic);
            if (null == methodDeclaration) {
                // Could be a "required" method in a Role. It's O.K.
                assert true;
            } else {
                final boolean accessOK = currentScope_.canAccessDeclarationWithAccessibility(methodDeclaration,
                        methodDeclaration.accessQualifier(), ctxGetStart.getLine());
                if (accessOK == false) {
                    errorHook6p2(ErrorIncidenceType.Fatal, ctxGetStart.getLine(), "Cannot access method `",
                            methodDeclaration.name(), "' with `", methodDeclaration.accessQualifier().asString(),
                            "' access qualifier.", "");
                }
            }
        } else {
            // Stumble elegantly
            retval = new ErrorExpression(object);
        }

        return retval;
    }

    @Override
    public <ExprType> Expression messageSend(final Token ctxGetStart, final ExprType ctx_abelianAtom,
            final Builtin_type_nameContext ctx_builtin_typeName) {
        // | expr '.' message
        // | message
        // Certified Pass 2 version.
        // REFACTOR! TODO

        MethodDeclaration methodDeclaration = null;
        Expression retval = null;
        final StaticScope nearestMethodScope = Expression.nearestEnclosingMethodScopeAround(currentScope_);
        final Type nearestEnclosingMegaType = Expression.nearestEnclosingMegaTypeOf(currentScope_);
        Expression object = messageSendGetObject(ctxGetStart, ctx_abelianAtom, ctx_builtin_typeName);

        Message message = parsingData_.popMessage();

        if (null == message) {
            return new ErrorExpression(object); // yuk. refactor.
        }

        if (null == nearestEnclosingMegaType && object instanceof NullExpression) {
            errorHook5p2(ErrorIncidenceType.Fatal, ctxGetStart.getLine(), "Invoking method `",
                    message.selectorName(), "' on implied object `this' in a non-object context.", "");
            ;
        } else if (null != object) {
            // For future reference, we don't want to do this if the
            // method is static. Of course, we can't in general know
            // that until we look it up, and we can't look it up
            // until we have its signature, and we can't have its
            // signature until we know whether it takes a "this"
            // parameter... Maybe the answer is to pass a parameter
            // even for static functions (the class object)
            message.addActualThisParameter(object);
        }

        Type objectType = null == object ? null : object.type();
        if (null == objectType) {
            objectType = parsingData_.globalScope().lookupTypeDeclaration("Object");
        }
        assert null != objectType;

        MethodSignature methodSignature = null;
        boolean isOKMethodSignature = false;

        if (objectType.name().equals("Class")) {
            // Static method invocation. The "object" is really a class name.

            assert object instanceof IdentifierExpression;
            final Type type = currentScope_.lookupTypeDeclarationRecursive(object.name());
            methodDeclaration = type.enclosedScope().lookupMethodDeclaration(message.selectorName(),
                    message.argumentList(), false);
            if (null == methodDeclaration) {
                methodDeclaration = type.enclosedScope().lookupMethodDeclarationWithConversionIgnoringParameter(
                        message.selectorName(), message.argumentList(), false, /*parameterToIgnore*/ null);
            }
            if (null == methodDeclaration) {
                errorHook5p2(ErrorIncidenceType.Fatal, ctxGetStart.getLine(), "Cannot find declaration of ",
                        object.name() + ".", message.selectorName(), ".");
            } else {
                methodSignature = methodDeclaration.signature();
                isOKMethodSignature = null != methodSignature;
            }
        } else if (objectType instanceof RoleType || objectType instanceof StagePropType) {
            Type wannabeContextType = nearestEnclosingMegaType;
            if (wannabeContextType instanceof RoleType) {
                final RoleType nearestEnclosingRoleOrStageProp = (RoleType) nearestEnclosingMegaType;
                wannabeContextType = Expression
                        .nearestEnclosingMegaTypeOf(nearestEnclosingRoleOrStageProp.enclosingScope());
                assert wannabeContextType instanceof ContextType;

                if (nearestEnclosingMegaType instanceof RoleType
                        || nearestEnclosingMegaType instanceof StagePropType) {
                    // Don't allow Role methods directly to invoke Context methods.
                    contextInvocationCheck(nearestEnclosingMegaType, message, objectType, wannabeContextType,
                            ctxGetStart);

                    // Don't allow Role methods directly to invoke other Roles' "requires" methods.
                    otherRolesRequiresInvocationCheck(nearestEnclosingMegaType, message, objectType,
                            wannabeContextType, ctxGetStart);

                    if (((RoleType) objectType).isArray()) {
                        if (object instanceof RoleArrayIndexExpression) {
                            // o.k.
                        } else if (object.name().equals("this")) {
                            // then it's not really a Role method, but a requires method
                            // (hope and pray)
                        } else {
                            // Then this is trying to invoke a Role vector method
                            // without specifying the individual vector method

                            // This makes sense only if we are calling a method in the same Role
                            // as holds this method
                            final String objectTypePathName = object.type().pathName();
                            if (nearestEnclosingMegaType.pathName().equals(objectTypePathName) == false) {
                                errorHook6p2(ErrorIncidenceType.Fatal, ctxGetStart.getLine(),
                                        "Trying to access method `", message.selectorName(), "' of another Role (`",
                                        object.name(), "') with indexing the Role vector to yield a Role-player.",
                                        "");
                            } else {
                                final IndexExpression theIndex = new IndexExpression(
                                        nearestEnclosingRoleOrStageProp.associatedDeclaration(), currentContext_);
                                object = new RoleArrayIndexExpression(object.name(), object, theIndex);

                                // Woops  message is wrong. Replace its "this"
                                message.replaceActualThisParameter(object);
                            }
                        }
                    }
                }
            } else if (wannabeContextType instanceof ContextType) {
                // We don't want Context methods to be able directly
                // to call requires methods of Roles
                final RoleType objectTypeAsRoleType = (RoleType) objectType;

                if (((RoleType) objectType).isArray()) {
                    if (object instanceof RoleArrayIndexExpression) {
                        // o.k.
                    } else {
                        // Then this is trying to invoke a Role vector method
                        // without specifying the individual vector method
                        errorHook5p2(ErrorIncidenceType.Fatal, ctxGetStart.getLine(),
                                "Trying to access a Role method `", message.selectorName(), "' on the vector ID `",
                                object.name() + "'.");
                    }
                }

                final RoleDeclaration roleDecl = (RoleDeclaration) objectTypeAsRoleType.associatedDeclaration();
                final MethodSignature signatureInRequiresSection = declarationForMessageFromRequiresSectionOfRole(
                        message, roleDecl);
                if (null != signatureInRequiresSection) {
                    // Is O.K. if it is also declared in the Role interface
                    final MethodSignature signatureInRoleInterface = roleDecl
                            .lookupPublishedSignatureDeclaration(signatureInRequiresSection);
                    if (null == signatureInRoleInterface) {
                        final StaticScope currentMethodScope = Expression
                                .nearestEnclosingMethodScopeAround(currentScope_);
                        errorHook6p2(ErrorIncidenceType.Fatal, ctxGetStart.getLine(), "Context script `",
                                currentMethodScope.associatedDeclaration().name(),
                                "' may enact only Role scripts. Script `",
                                message.selectorName() + message.argumentList().selflessGetText(),
                                "' is an instance script from a class and is inaccessible to Context `",
                                wannabeContextType.name() + "'.");
                    }
                }
            }

            // Look this thing up in the "required" interface to see
            // if it's really a Role method or just a latently bound
            // instance method in an object bound to this role
            assert objectType instanceof RoleType;
            final RoleType roleType = (RoleType) objectType;
            methodSignature = roleType.lookupMethodSignatureDeclaration(message.selectorName());
            if (null != methodSignature) {
                // Then it may be in the "required" declarations and is NOT a role method per se
                isOKMethodSignature = true;

                // But this check may be useful...
                final MethodSignature publishedSignature = roleType.associatedDeclaration()
                        .lookupPublishedSignatureDeclaration(methodSignature);
                if (null != publishedSignature && publishedSignature.isUnusedInThisContext()) {
                    final FormalParameterList formalParameterList = publishedSignature.formalParameterList();
                    errorHook6p2(ErrorIncidenceType.Fatal, ctxGetStart.getLine(), "Script `",
                            message.selectorName() + formalParameterList.selflessGetText(),
                            "' is declared as unused in `", objectType.name(), "' at line ",
                            Integer.toString(publishedSignature.lineNumber()) + ".");
                }
            } else {
                // If we're calling from a Context script to a script of one of its Roles,
                // we want to pass "this" in the "current$context" slot. Otherwise, if it's
                // from another Role, just use "current$context". There should be no other
                // possibilities
                final Type enclosingType = Expression.nearestEnclosingMegaTypeOf(currentScope_);

                String nameOfContextIdentifier;
                if (enclosingType instanceof ContextType) {
                    nameOfContextIdentifier = "this";
                } else if (enclosingType instanceof RoleType) {
                    nameOfContextIdentifier = "current$context";
                } else {
                    nameOfContextIdentifier = "current$context"; // arbitrary.
                    assert false;
                }

                final Expression currentContext = new IdentifierExpression(nameOfContextIdentifier,
                        wannabeContextType, nearestMethodScope, ctxGetStart.getLine());
                final ActualArgumentList saveArgumentList = message.argumentList().copy();
                message.argumentList().addFirstActualParameter(currentContext);
                currentContext.setResultIsConsumed(true);

                // NOTE: Leaves methodSignature null.
                // We need it for call of checkForMessageSendViolatingConstness below.
                methodDeclaration = objectType.enclosedScope().lookupMethodDeclaration(message.selectorName(),
                        message.argumentList(), false);
                if (null != methodDeclaration) {
                    // Null check is related to error stumbling
                    methodSignature = methodDeclaration.signature();
                } else {
                    // It's a Role. Could be a call to assert or something like that
                    // in the base class
                    final ClassDeclaration objectDecl = currentScope_.lookupClassDeclarationRecursive("Object");
                    assert null != objectDecl;
                    methodDeclaration = processReturnTypeLookupMethodDeclarationIgnoringRoleStuffIn(objectDecl,
                            message.selectorName(), message.argumentList());

                    if (null != methodDeclaration) {
                        // I THINK that the right thing to do at this point is to pull
                        // the context out of the signature. Luckily, we copied the
                        // parameter list above...

                        message = new Message(message.selectorName(), saveArgumentList, message.lineNumber(),
                                message.enclosingMegaType());
                        methodSignature = methodDeclaration.signature();
                    }
                }
            }
        } else if (objectType instanceof ClassType || objectType instanceof ContextType) {
            final ClassOrContextType classObjectType = (ClassOrContextType) objectType;
            final StaticScope classScope = null == nearestEnclosingMegaType ? null
                    : nearestEnclosingMegaType.enclosedScope();
            final TemplateInstantiationInfo templateInstantiationInfo = null == classScope ? null
                    : classScope.templateInstantiationInfo();

            final ActualOrFormalParameterList argumentList = null != message && null != message.argumentList()
                    ? message.argumentList().mapTemplateParameters(templateInstantiationInfo)
                    : null;
            methodDeclaration = null != classObjectType && null != classObjectType.enclosedScope()
                    ? classObjectType.enclosedScope().lookupMethodDeclarationRecursive(message.selectorName(),
                            argumentList, false)
                    : null;
            if (null == methodDeclaration) {
                // Check the base class
                if (null != classObjectType && null != classObjectType.enclosedScope()) {
                    ClassType baseClassType = classObjectType.baseClass();
                    while (null != baseClassType) {
                        final StaticScope baseClassScope = baseClassType.enclosedScope();
                        assert null != baseClassScope;
                        methodDeclaration = baseClassScope.lookupMethodDeclarationWithConversionIgnoringParameter(
                                message.selectorName(), argumentList, false, /*parameterToIgnore*/ null);
                        if (null != methodDeclaration) {
                            break;
                        }
                        baseClassType = baseClassType.baseClass();
                    }
                }
                if (null == methodDeclaration) {
                    // If we're inside of a template, many argument types won't match.
                    // Try anyhow and see if we can find something.

                    methodDeclaration = null != classObjectType && null != classObjectType.enclosedScope()
                            ? classObjectType.enclosedScope().lookupMethodDeclarationRecursive(
                                    message.selectorName(), argumentList, true)
                            : null;
                    if (null == methodDeclaration) {
                        // Mainly for error recovery (bad argument to method / method not declared)
                        errorHook5p2(ErrorIncidenceType.Fatal, ctxGetStart.getLine(), "Script `", message.getText(),
                                "' not declared in class `", classObjectType.name() + "'.");
                        return new ErrorExpression(null); // punt
                    } else {
                        methodSignature = methodDeclaration.signature();
                    }
                } else {
                    methodSignature = methodDeclaration.signature();
                }
            } else {
                methodSignature = methodDeclaration.signature();
            }
        } else if (objectType instanceof BuiltInType) {
            final BuiltInType classObjectType = (BuiltInType) objectType;
            final StaticScope classScope = null == nearestEnclosingMegaType ? null
                    : nearestEnclosingMegaType.enclosedScope();
            final TemplateInstantiationInfo templateInstantiationInfo = null == classScope ? null
                    : classScope.templateInstantiationInfo();
            final ActualOrFormalParameterList argumentList = null != message && null != message.argumentList()
                    ? message.argumentList().mapTemplateParameters(templateInstantiationInfo)
                    : null;
            methodDeclaration = null != classObjectType && null != classObjectType.enclosedScope()
                    ? classObjectType.enclosedScope().lookupMethodDeclarationRecursive(message.selectorName(),
                            argumentList, false)
                    : null;
            if (null == methodDeclaration) {
                // If we're inside of a template, many argument types won't match.
                // Try anyhow and see if we can find something.
                methodDeclaration = null != classObjectType && null != classObjectType.enclosedScope()
                        ? classObjectType.enclosedScope().lookupMethodDeclarationRecursive(message.selectorName(),
                                argumentList, true)
                        : null;
                if (null == methodDeclaration) {
                    // Mainly for error recovery (bad argument to method / method not declared)
                    errorHook5p2(ErrorIncidenceType.Fatal, ctxGetStart.getLine(), "Script `", message.getText(),
                            "' not declared in class `", classObjectType.name() + "'.");
                    return new ErrorExpression(null); // punt
                } else {
                    methodSignature = methodDeclaration.signature();
                }
            } else {
                methodSignature = methodDeclaration.signature();
            }
        } else if (objectType instanceof InterfaceType) {
            final InterfaceType classObjectType = (InterfaceType) objectType;
            final ActualOrFormalParameterList argumentList = message.argumentList();
            final String methodSelectorName = message.selectorName();
            methodSignature = null != classObjectType
                    ? classObjectType.lookupMethodSignature(methodSelectorName, argumentList)
                    : null;
            if (null == methodSignature) {
                // Try again, ignoring type of this (e.g., for Interface types)
                methodSignature = null != classObjectType
                        ? classObjectType.lookupMethodSignatureWithConversionIgnoringParameter(
                                methodSelectorName, argumentList, "this")
                        : null;
                if (null == methodSignature) {
                    // Mainly for error recovery (bad argument to method / method not declared)
                    if (argumentList.isntError()) {
                        errorHook5p2(ErrorIncidenceType.Fatal, ctxGetStart.getLine(), "Script `",
                                methodSelectorName + argumentList.getText(), "' not declared in Interface `",
                                classObjectType.name() + "'.");
                    }
                } else {
                    isOKMethodSignature = true;
                }
            } else {
                isOKMethodSignature = true;
            }
        } else if (objectType.name().endsWith("_$array") && objectType instanceof ArrayType) {
            // This is part of the endeavor to add method invocations to
            // naked array object appearances (e.g., size())
            if (message.selectorName().equals("size")) {
                methodSignature = new MethodSignature(message.selectorName(),
                        StaticScope.globalScope().lookupTypeDeclaration("int"), AccessQualifier.PublicAccess,
                        (int) message.lineNumber(), false);
                methodSignature.setHasConstModifier(true);
            } else if (message.selectorName().equals("at")) {
                methodSignature = new MethodSignature(message.selectorName(), message.returnType(),
                        AccessQualifier.PublicAccess, (int) message.lineNumber(), false);
                methodSignature.setHasConstModifier(true);
            } else if (message.selectorName().equals("atPut")) {
                methodSignature = new MethodSignature(message.selectorName(),
                        StaticScope.globalScope().lookupTypeDeclaration("void"), AccessQualifier.PublicAccess,
                        (int) message.lineNumber(), false);
                methodSignature.setHasConstModifier(false);
            }
            isOKMethodSignature = true;
        }

        Type returnType = this.processReturnType(ctxGetStart, object, objectType, message);

        if (null != returnType && returnType instanceof TemplateParameterType) {
            // Is a template type. Change the return type into a bona fide type here
            final StaticScope objectScope = objectType.enclosedScope();
            final TemplateInstantiationInfo newTemplateInstantiationInfo = objectScope.templateInstantiationInfo();
            returnType = newTemplateInstantiationInfo.classSubstitionForTemplateTypeNamed(returnType.name());
        }

        if (objectType.name().equals(message.selectorName())) {
            errorHook5p2(ErrorIncidenceType.Fatal, ctxGetStart.getLine(), "Cannot 'call' constructor of ",
                    objectType.name(), ". Use 'new' instead.", "");
        }

        assert (null != message); // just assuming...
        final String methodSelectorName = message.selectorName();

        if (null != methodDeclaration) {
            final List<String> invalidMethodSelectors = message.validInRunningEnviroment(methodDeclaration);
            if (0 < invalidMethodSelectors.size()) {
                errorHook5p2(ErrorIncidenceType.Fatal, ctxGetStart.getLine(), "The parameters to script `",
                        methodSelectorName + message.argumentList().selflessGetText(),
                        "' have scripts that are unavailable outside this Context, ",
                        "though some formal parameters of " + methodSelectorName
                                + " presume they are available (they are likely Role scripts):");
                for (final String badMethod : invalidMethodSelectors) {
                    errorHook5p2(ErrorIncidenceType.Fatal, ctxGetStart.getLine(), "\t", badMethod, "", "");
                }
            }
        } else if (null == methodDeclaration && isOKMethodSignature == false) {
            if (message.argumentList().isntError()) {
                errorHook5p2(ErrorIncidenceType.Fatal, ctxGetStart.getLine(), "Script `",
                        methodSelectorName + message.argumentList().selflessGetText(), "' not declared in class ",
                        "classname (dubious error  see other messages)");
            }
        }

        assert null != returnType;
        assert null != object;
        assert null != message;

        message.setReturnType(returnType);

        retval = messageSendGenerateCall(ctxGetStart, object, methodDeclaration, message, returnType,
                methodSignature);

        return retval;
    }

    @Override
    protected MethodDeclaration processReturnTypeLookupMethodDeclarationIn(final TypeDeclaration classDecl,
            final String methodSelectorName, final ActualOrFormalParameterList parameterList) {
        // Pass 2 / 3 version turns on signature checking
        final StaticScope classOrRoleOrWhateverScope = classDecl.enclosedScope();

        // Give exact match the first chance
        MethodDeclaration retval = classOrRoleOrWhateverScope.lookupMethodDeclarationIgnoringParameter(
                methodSelectorName, parameterList, "this", /* conversionAllowed = */ false);
        if (null == retval) {
            // If exact match doesn't work, try promition
            retval = classOrRoleOrWhateverScope.lookupMethodDeclarationIgnoringParameter(methodSelectorName,
                    parameterList, "this", /* conversionAllowed = */ true);
        }
        return retval;
    }

    @Override
    protected MethodDeclaration processReturnTypeLookupMethodDeclarationIgnoringRoleStuffIn(
            final TypeDeclaration classDecl, final String methodSelectorName,
            final ActualOrFormalParameterList parameterList) {
        // Pass 2 / 3 version turns on signature checking
        final StaticScope classScope = classDecl.enclosedScope();
        return classScope.lookupMethodDeclarationIgnoringRoleStuff(methodSelectorName, parameterList);
    }

    @Override
    protected MethodDeclaration processReturnTypeLookupMethodDeclarationUpInheritanceHierarchy(
            final TypeDeclaration classDecl, final String methodSelectorName,
            final ActualOrFormalParameterList parameterList) {
        // Pass 2 / 3 version turns on signature checking
        StaticScope classScope = classDecl.enclosedScope();
        MethodDeclaration retval = classScope.lookupMethodDeclarationIgnoringParameter(methodSelectorName,
                parameterList, "this", true);
        if (null == retval) {
            if (classDecl instanceof ClassDeclaration || classDecl instanceof ContextDeclaration) { // should be
                final ObjectSubclassDeclaration classDeclAsClassOrContextDecl = (ObjectSubclassDeclaration) classDecl;
                final ClassDeclaration baseClassDeclaration = classDeclAsClassOrContextDecl.baseClassDeclaration();
                if (null != baseClassDeclaration) {
                    classScope = baseClassDeclaration.enclosedScope();
                    retval = classScope.lookupMethodDeclarationIgnoringParameter(methodSelectorName, parameterList,
                            "this", /* conversionAllowed = */ false);
                } else {
                    retval = null;
                }
            }
        }
        return retval;
    }

    private void typeCheckHelperForRoleMismatches(final FormalParameterList formals,
            final ActualArgumentList actuals, final MethodDeclaration mdecl, final TypeDeclaration classdecl,
            final String parameterToIgnore, final Token ctxGetStart) {

        // This is for checking agreement with functions like assert

        int actualParameterIndex = 0, formalParameterIndex = 0;
        final int numberOfFormalParameters = formals.count(),
                numberOfActualParameters = (null == actuals) ? 0 : actuals.count();
        if (null == actuals) {
            if (numberOfActualParameters != 0) {
                errorHook5p2(ErrorIncidenceType.Fatal, ctxGetStart.getLine(),
                        "Number of arguments in enactment of script `", mdecl.name(),
                        "' does not match declaration of `", mdecl.name() + "'.");
                int lineNumber = mdecl.lineNumber();
                if (0 == lineNumber) {
                    // e.g., for a built-in type
                    lineNumber = ctxGetStart.getLine();
                }
                errorHook5p2(ErrorIncidenceType.Fatal, lineNumber, "\tMethod ", mdecl.name(), " is declared in ",
                        classdecl.name());
            }
        } else {
            // Strip off all the stuff up front
            String actualParameterName = actuals.nameOfParameterAtPosition(actualParameterIndex);
            while (actualParameterName.equals("this") || actualParameterName.equals("t$this")
                    || actualParameterName.equals("current$context")
                    || actualParameterName.equals("current$role")) {
                actualParameterIndex++;
                if (actualParameterIndex < numberOfActualParameters) {
                    actualParameterName = actuals.nameOfParameterAtPosition(actualParameterIndex);
                } else {
                    break;
                }
            }

            String formalParameterName = formals.nameOfParameterAtPosition(formalParameterIndex);
            while (formalParameterName.equals("this") || formalParameterName.equals("t$this")
                    || formalParameterName.equals("current$context")
                    || formalParameterName.equals("current$role")) {
                formalParameterIndex++;
                if (formalParameterIndex < numberOfFormalParameters) {
                    formalParameterName = formals.nameOfParameterAtPosition(formalParameterIndex);
                } else {
                    break;
                }
            }

            Expression actualParameter = null;
            Type actualParameterType = null;
            Declaration formalParameter = null;
            Type formalParameterType = null;

            while (formalParameterIndex < numberOfFormalParameters
                    && actualParameterIndex < numberOfActualParameters) {
                boolean parametersMatch = true;

                final Object rawActualParameter = actuals.argumentAtPosition(actualParameterIndex);
                if (rawActualParameter == null || (rawActualParameter instanceof Expression) == false) {
                    assert rawActualParameter != null && rawActualParameter instanceof Expression;
                }
                actualParameter = (Expression) rawActualParameter;
                actualParameterType = actualParameter.type();

                formalParameterName = formals.nameOfParameterAtPosition(formalParameterIndex);
                formalParameter = formals.parameterAtPosition(formalParameterIndex);
                assert formalParameter instanceof ObjectDeclaration || formalParameter.isError();
                formalParameterType = formalParameter.type();

                if (actualParameterType.enclosedScope() == formalParameterType.enclosedScope()) {
                    actualParameterIndex++;
                    formalParameterIndex++;
                } else if (actualParameterType.isBaseClassOf(formalParameterType)) {
                    actualParameterIndex++;
                    formalParameterIndex++;
                } else if (formalParameterType.canBeConvertedFrom(actualParameterType)) {
                    actualParameterIndex++;
                    formalParameterIndex++;
                } else {
                    final Type enclosingType = Expression.nearestEnclosingMegaTypeOf(currentScope_);
                    if (enclosingType instanceof TemplateType) {
                        // It could just work. This is just the template we're processing. Check things out
                        // later in the class instead.
                        actualParameterIndex++;
                        formalParameterIndex++;
                    } else {
                        parametersMatch = false;
                    }
                }

                if (false == parametersMatch && actualParameter.isntError() && formalParameter.isntError()) {
                    final String actualParamMsg = actualParameter.getText() + "' (" + actualParameterType.name()
                            + ")";
                    final String formalParamMsg = "`" + formalParameter.name() + "' (" + formalParameterType.name()
                            + " " + formalParameter.name() + ")";
                    errorHook6p2(ErrorIncidenceType.Fatal, ctxGetStart.getLine(), "Type of actual parameter `",
                            actualParamMsg, " in call of `", mdecl.name(),
                            "' does not match type of formal parameter ", formalParamMsg);
                }
            }

            if (formalParameterIndex != numberOfFormalParameters
                    || actualParameterIndex != numberOfActualParameters) {
                if (null != mdecl) {
                    errorHook5p2(ErrorIncidenceType.Fatal, ctxGetStart.getLine(),
                            "Number of arguments in enactment of script `", mdecl.name(),
                            "' does not match declaration of `", mdecl.name() + "'.");
                }

                int lineNumber = null == mdecl ? 0 : mdecl.lineNumber();
                if (0 == lineNumber) {
                    // e.g., for a built-in type
                    lineNumber = ctxGetStart.getLine();
                }
                if (null != classdecl && null != mdecl) {
                    errorHook5p2(ErrorIncidenceType.Fatal, lineNumber, "\tScript ", mdecl.name(),
                            " is declared in ", classdecl.name());
                }
            }
        }
    }

    protected void typeCheckIgnoringParameterNormal(final FormalParameterList formals,
            final ActualArgumentList actuals, final MethodDeclaration mdecl, final TypeDeclaration classdecl,
            final String parameterToIgnore, final Token ctxGetStart) {
        final long numberOfActualParameters = actuals.count();
        final long numberOfFormalParameters = formals.count();

        if (numberOfFormalParameters != numberOfActualParameters) {
            if (formals.containsVarargs()) {
                if (numberOfActualParameters < numberOfFormalParameters) {
                    errorHook5p2(ErrorIncidenceType.Fatal, ctxGetStart.getLine(),
                            "Number of arguments in enactment of script `", mdecl.name(),
                            "' must be at least as many as in declaration of the script (",
                            String.format("%d", numberOfFormalParameters - 1) + ").");
                } else {
                    ; // O.K.
                }
            } else {
                errorHook5p2(ErrorIncidenceType.Fatal, ctxGetStart.getLine(),
                        "Number of arguments in enactment of script `", mdecl.name(),
                        "' does not match declaration of `", mdecl.name() + "'.");
                int lineNumber = mdecl.lineNumber();
                if (0 == lineNumber) {
                    // e.g., for a built-in type
                    lineNumber = ctxGetStart.getLine();
                }
                if (null != classdecl) {
                    errorHook5p2(ErrorIncidenceType.Fatal, lineNumber, "\tMethod ", mdecl.name(),
                            " is declared in ", classdecl.name());
                }
            }
        } else {

            for (int j = 0; j < numberOfActualParameters; j++) {
                final Object rawParameter = actuals.argumentAtPosition(j);
                assert rawParameter != null && rawParameter instanceof Expression;
                final Expression actualParameter = (Expression) rawParameter;
                final Type actualParameterType = actualParameter.type();

                final Declaration formalParameter = formals.parameterAtPosition(j);
                final Type formalParameterType = formalParameter.type();

                if (formalParameterType instanceof VarargsType) {
                    break;
                } else if (null == formalParameterType) {
                    ; // formalParameter is likely of ErrorDeclaration type  just skip it
                } else if (formalParameterType.canBeConvertedFrom(actualParameterType)) {
                    continue;
                } else if (formalParameter.name().equals(parameterToIgnore)) {
                    continue;
                } else {
                    final Type enclosingType = Expression.nearestEnclosingMegaTypeOf(currentScope_);
                    if (enclosingType instanceof TemplateType) {
                        // It could just work. This is just the template we're processing. Check things out
                        // later in the class instead.
                    } else {
                        // See if it could be an interface of the class
                        boolean isOK = false;
                        if (actualParameterType instanceof ClassOrContextType) {
                            final List<InterfaceType> interfaceTypes = ((ClassOrContextType) actualParameterType)
                                    .interfaceTypes();
                            for (final Type alternativeActualParameterType : interfaceTypes) {
                                if (formalParameterType.canBeConvertedFrom(alternativeActualParameterType)) {
                                    isOK = true;
                                    break;
                                }
                            }
                        }
                        if (false == isOK && actualParameter.isntError() && formalParameter.isntError()) {
                            final String actualParamMsg = actualParameter.getText() + "' ("
                                    + actualParameterType.name() + ")";
                            final String formalParamMsg = "`" + formalParameter.name() + "' ("
                                    + formalParameterType.name() + " " + formalParameter.name() + ")";
                            errorHook6p2(ErrorIncidenceType.Fatal, ctxGetStart.getLine(),
                                    "Type of actual parameter `", actualParamMsg, " in call of `", mdecl.name(),
                                    "' does not match type of formal parameter ", formalParamMsg);
                        }
                    }
                }
            }
        }
    }

    @Override
    protected void typeCheckIgnoringParameter(final FormalParameterList formals, final ActualArgumentList actuals,
            final MethodDeclaration mdecl, final TypeDeclaration classdecl, final String parameterToIgnore,
            final Token ctxGetStart, final boolean roleHint) {

        // roleHint is set if the method was successfully found by turning off
        // any consideration of Role parameters, like current$context. This
        // typically applies in the ancillary lookup of the assert API in
        // class Object when invoked through a Role pointer.
        if (roleHint) {
            typeCheckHelperForRoleMismatches(formals, actuals, mdecl, classdecl, parameterToIgnore, ctxGetStart);
        } else {
            typeCheckIgnoringParameterNormal(formals, actuals, mdecl, classdecl, parameterToIgnore, ctxGetStart);
        }
    }

    private Expression canBeAScriptEnactment(final StaticScope megaTypeEnclosedScope, final String scriptName,
            final int lineNumber) {
        // Eiffel-style script (feature) enactment
        Expression retval = null;
        final Type enclosingMegaType = Expression.nearestEnclosingMegaTypeOf(currentScope_);
        if (megaTypeEnclosedScope instanceof StaticRoleScope) {
            final RoleDeclaration roleDecl = (RoleDeclaration) megaTypeEnclosedScope.associatedDeclaration();
            MethodSignature methodSignature = roleDecl.lookupRequiredMethodSignatureDeclaration(scriptName);
            if (null == methodSignature) {
                methodSignature = roleDecl.lookupPublishedSignatureDeclaration(scriptName);
            }
            if (null != methodSignature) {
                if (0 == methodSignature.formalParameterList().userParameterCount()) {
                    // o.k.
                    final ActualArgumentList argumentList = new ActualArgumentList();

                    // There is no current$context for a published method declaration or a requires declaration.
                    /*
                    final Expression currentContext = new IdentifierExpression("current$context", roleDecl.contextDeclaration().type(),
                          currentContext_.enclosedScope(), lineNumber);
                    argumentList.addActualArgument(currentContext);
                    */
                    final Expression self = new IdentifierExpression(roleDecl.name(), roleDecl.type(),
                            currentContext_.enclosedScope(), lineNumber);
                    argumentList.addActualArgument(self);

                    final Message message = new Message(scriptName, argumentList, lineNumber, enclosingMegaType);
                    final MethodInvocationEnvironmentClass originMethodClass = currentScope_
                            .methodInvocationEnvironmentClass();
                    final MethodInvocationEnvironmentClass targetMethodClass = self.type().enclosedScope()
                            .methodInvocationEnvironmentClass();
                    retval = new MessageExpression(self, message, methodSignature.returnType(), lineNumber,
                            /*isStatic*/ false, originMethodClass, targetMethodClass, true);
                }
            }
        } else if (null != megaTypeEnclosedScope && enclosingMegaType instanceof ContextType) {
            final ContextDeclaration contextDecl = (ContextDeclaration) enclosingMegaType.enclosedScope()
                    .associatedDeclaration();
            final ActualArgumentList argumentList = new ActualArgumentList();
            final Expression self = new IdentifierExpression("this", contextDecl.type(), currentScope_, lineNumber);
            argumentList.addActualArgument(self);
            final MethodDeclaration methodDeclaration = currentContext_.enclosedScope()
                    .lookupMethodDeclaration(scriptName, argumentList, false);
            if (null != methodDeclaration) {
                final MethodSignature methodSignature = methodDeclaration.signature();
                if (0 == methodSignature.formalParameterList().userParameterCount()) {
                    // o.k.  Eiffel-style feature invocation
                    final Message message = new Message(scriptName, argumentList, lineNumber, enclosingMegaType);
                    final MethodInvocationEnvironmentClass originMethodClass = currentScope_
                            .methodInvocationEnvironmentClass();
                    final MethodInvocationEnvironmentClass targetMethodClass = self.type().enclosedScope()
                            .methodInvocationEnvironmentClass();
                    retval = new MessageExpression(self, message, methodSignature.returnType(), lineNumber,
                            /*isStatic*/ false, originMethodClass, targetMethodClass, true);
                }
            }
        }
        return retval;
    }

    @Override
    public Expression idExpr(final TerminalNode ctxJAVA_ID, final Token ctxGetStart) {
        // | JAVA_ID
        // Special version for pass 2 and 3

        Type type = null;
        Expression retval = null;
        RoleDeclaration aRoleDecl = null;
        StaticScope declaringScope = null;
        final StaticScope globalScope = StaticScope.globalScope();
        final String idText = ctxJAVA_ID.getText();
        ObjectDeclaration objectDecl = null;

        final ObjectDeclaration objdecl = currentScope_.lookupObjectDeclarationRecursive(idText);
        if (null != objdecl) {
            type = objdecl.type();
            declaringScope = objdecl.enclosingScope();
            final Type enclosingMegaType = Expression.nearestEnclosingMegaTypeOf(currentScope_);
            StaticScope megaTypeScope = null;
            if (null != enclosingMegaType) {
                megaTypeScope = enclosingMegaType.enclosedScope();
            } else {
                megaTypeScope = globalScope;
            }

            if (declaringScope == megaTypeScope) {
                // Then it's a member of an object of the current class / context
                // Probably better to make it a qualified identifier
                final StaticScope enclosingMethodScope = Expression
                        .nearestEnclosingMethodScopeAround(currentScope_);
                final Declaration associatedDeclaration = declaringScope.associatedDeclaration();
                final IdentifierExpression self = new IdentifierExpression("this", associatedDeclaration.type(),
                        enclosingMethodScope, ctxGetStart.getLine());
                retval = new QualifiedIdentifierExpression(self, idText, type);

                // Further checks
                this.ensureNotDuplicatedInBaseClass(associatedDeclaration, idText, ctxGetStart.getLine());
            } else {
                retval = new IdentifierExpression(ctxJAVA_ID.getText(), type, declaringScope,
                        ctxGetStart.getLine());
            }

            // We don't want Role scope to be able to access symbols at
            // Context scope. Do a specific check for that and issue
            // a non-compliance warning

            final StaticScope currentProcScope = Expression.nearestEnclosingMethodScopeAround(currentScope_);
            if (null != currentProcScope) {
                final MethodDeclaration currentMethod = (MethodDeclaration) currentProcScope
                        .associatedDeclaration();

                // Is it a Role method?
                final StaticScope methodsEnclosingScope = currentMethod.enclosingScope();
                final Declaration enclosingMegaTypeDeclaration = methodsEnclosingScope.associatedDeclaration();
                if (enclosingMegaTypeDeclaration instanceof RoleDeclaration) {
                    final Declaration symbolsEnclosingMegaType = declaringScope.associatedDeclaration();
                    if (symbolsEnclosingMegaType instanceof ContextDeclaration) {
                        // Then there is code within the Role method accessing a symbol
                        // in the surrounding Context. Maybe a no-no.
                        errorHook6p2(ErrorIncidenceType.Noncompliant, ctxGetStart.getLine(),
                                "NONCOMPLIANT: Attempt to access Context member `", idText,
                                "' from within scope of Role script `",
                                enclosingMegaTypeDeclaration.name() + "." + currentMethod.name(),
                                "'. Roles may not directly access Context data. ",
                                " Consider binding the Context to one of its own Roles instead.");
                    }
                }
            }

            assert null != retval;
        } else if (null != currentScope_.lookupClassDeclarationRecursive(idText)) {
            // Could be a reference to a class itself (like System)
            type = StaticScope.globalScope().lookupTypeDeclaration("Class");
            declaringScope = StaticScope.globalScope();
            retval = new IdentifierExpression(idText, type, declaringScope, ctxGetStart.getLine());
        } else if (null != (aRoleDecl = super.isRoleAssignmentWithinContext(idText))) {
            type = aRoleDecl.type();
            declaringScope = aRoleDecl.enclosingScope();
            retval = new IdentifierExpression(ctxJAVA_ID.getText(), type, declaringScope, ctxGetStart.getLine());
        } else if (null != Expression.nearestEnclosingMegaTypeOf(currentScope_)
                && null != Expression.nearestEnclosingMegaTypeOf(currentScope_).enclosedScope()
                && null != (objectDecl = Expression.nearestEnclosingMegaTypeOf(currentScope_).enclosedScope()
                        .lookupObjectDeclarationRecursive(idText))) {
            // done  get outta here
            final IdentifierExpression self = new IdentifierExpression("this", // name
                    Expression.nearestEnclosingMegaTypeOf(currentScope_), // type of identifier
                    Expression.nearestEnclosingMethodScopeAround(currentScope_), // scope where *declared*
                    ctxGetStart.getLine());
            self.setResultIsConsumed(true);
            retval = new QualifiedIdentifierExpression(self, idText, objectDecl.type());
        } else if (null != Expression.nearestEnclosingMegaTypeOf(currentScope_)
                && null != Expression.nearestEnclosingMegaTypeOf(currentScope_).enclosedScope()
                && null != (aRoleDecl = Expression.nearestEnclosingMegaTypeOf(currentScope_).enclosedScope()
                        .lookupRoleOrStagePropDeclarationRecursive(idText))) {
            // done  get outta here
            final IdentifierExpression currentContext = new IdentifierExpression("current$context",
                    Expression.nearestEnclosingMegaTypeOf(aRoleDecl.enclosedScope()),
                    Expression.nearestEnclosingMethodScopeAround(currentScope_), ctxGetStart.getLine());
            currentContext.setResultIsConsumed(true);
            retval = new QualifiedIdentifierExpression(currentContext, idText, aRoleDecl.type());
        } else {
            final StaticScope possibleMethodScope = Expression.nearestEnclosingMethodScopeAround(currentScope_);
            final StaticScope possibleRoleScope = null == possibleMethodScope ? null
                    : possibleMethodScope.parentScope();
            final StaticScope possibleContextScope = null == possibleRoleScope ? null
                    : possibleRoleScope.parentScope();
            final Declaration associatedDeclaration = null == possibleContextScope ? null
                    : possibleContextScope.associatedDeclaration();

            if (null != possibleRoleScope && (idText.equals("index") || idText.equals("lastIndex"))) {
                // It's O.K.
                final RoleDeclaration roleDeclaration = (RoleDeclaration) possibleRoleScope.associatedDeclaration();
                if (roleDeclaration.isArray()) {
                    retval = idText.equals("index")
                            ? new IndexExpression(roleDeclaration, (ContextDeclaration) associatedDeclaration)
                            : new LastIndexExpression(roleDeclaration, (ContextDeclaration) associatedDeclaration);
                } else {
                    retval = new ErrorExpression(null);
                    errorHook5p2(ErrorIncidenceType.Fatal, ctxGetStart.getLine(), "The identifier `", idText,
                            "' may be invoked only from a method of an element of a Role vector.", "");
                }
            } else if (associatedDeclaration == currentContext_) {
                if (null != possibleContextScope) {
                    final RoleDeclaration roleDecl = possibleContextScope.lookupRoleOrStagePropDeclaration(idText);
                    if (null == roleDecl) {
                        // Check to see if it is a script invocation
                        if (null == (retval = canBeAScriptEnactment(possibleRoleScope, idText,
                                ctxGetStart.getLine()))) {
                            errorHook6p2(ErrorIncidenceType.Fatal, ctxGetStart.getLine(), "Object `", idText,
                                    "' is not declared in scope `", currentScope_.name(), "'.", "");
                            type = new ErrorType();
                            retval = new ErrorExpression(null);
                        }
                    } else {
                        // it's O.K. - maybe. Can be used as an L-value in an assignment. R-value, too, I guess
                        type = possibleContextScope.lookupTypeDeclaration(idText);
                        declaringScope = roleDecl.enclosingScope();
                        retval = new IdentifierExpression(ctxJAVA_ID.getText(), type, declaringScope,
                                ctxGetStart.getLine());
                    }
                } else {
                    errorHook6p2(ErrorIncidenceType.Fatal, ctxGetStart.getLine(), "Object `", idText,
                            "' is not declared in scope `", currentScope_.name(), "'.", "");
                    type = new ErrorType();
                    retval = new IdentifierExpression(ctxJAVA_ID.getText(), type, declaringScope,
                            ctxGetStart.getLine());
                    retval = new ErrorExpression(retval);
                }
            } else {
                // Could be a base class reference
                retval = this.lookToBaseClassForHelp(idText, ctxGetStart.getLine(), currentScope_);

                // That was about the last chance
                if (null == retval) {
                    // How about believing it could be a method call?
                    retval = canBeAScriptEnactment(possibleContextScope, idText, ctxGetStart.getLine());
                    if (null == retval) {
                        errorHook6p2(ErrorIncidenceType.Fatal, ctxGetStart.getLine(), "Object `", idText,
                                "' is not declared in scope `", currentScope_.name(), "'.", "");
                        type = new ErrorType();
                        retval = new ErrorExpression(null);
                    }
                }
            }
        }
        return retval;
    }

    private void ensureNotDuplicatedInBaseClass(final Declaration associatedDeclaration, final String idName,
            int lineNumber) {
        if (associatedDeclaration instanceof ClassDeclaration) {
            // See if there is a base declaration
            final ClassDeclaration cdecl = (ClassDeclaration) associatedDeclaration;
            final ClassDeclaration baseClassDeclaration = cdecl.baseClassDeclaration();
            if (null != baseClassDeclaration) {
                // See if the base class also has this name
                final StaticScope baseClassScope = baseClassDeclaration.enclosedScope();
                final ObjectDeclaration baseClassInstance = baseClassScope.lookupObjectDeclaration(idName);
                if (null != baseClassInstance) {
                    // Hmmm. It also exists in the base class
                    final String lastPartOfMessage = baseClassDeclaration.name() + "'.";
                    errorHook6p2(ErrorIncidenceType.Fatal, lineNumber, "Object declaration `", idName,
                            "' appears both in class `", associatedDeclaration.name(), "' and in base class `",
                            lastPartOfMessage);
                    errorHook5p2(ErrorIncidenceType.Fatal, lineNumber,
                            "  (The same identifier name may not appear multiple times in the same run-time scope.)",
                            "", "", "");
                }
            }
        }
    }

    private Expression lookToBaseClassForHelp(final String idName, int lineNumber, StaticScope scope) {
        Expression retval = null;
        final Type enclosingMegaType = Expression.nearestEnclosingMegaTypeOf(scope);
        StaticScope megaTypeScope = null;
        if (null != enclosingMegaType) {
            megaTypeScope = enclosingMegaType.enclosedScope();
        } else {
            megaTypeScope = StaticScope.globalScope();
        }
        final Declaration associatedDeclaration = megaTypeScope.associatedDeclaration();
        if (associatedDeclaration instanceof ClassDeclaration) {
            // See if there is a base declaration
            final ClassDeclaration cdecl = (ClassDeclaration) associatedDeclaration;
            final ClassDeclaration baseClassDeclaration = cdecl.baseClassDeclaration();
            if (null != baseClassDeclaration) {
                // See if the base class also has this name
                final StaticScope baseClassScope = baseClassDeclaration.enclosedScope();
                final ObjectDeclaration baseClassInstance = baseClassScope.lookupObjectDeclaration(idName);
                if (null != baseClassInstance) {
                    // Good. It also exists in the base class
                    if (baseClassInstance.accessQualifier_ == AccessQualifier.PublicAccess) {
                        final Type type = baseClassInstance.type();
                        final StaticScope nearestEnclosingMethodScope = Expression
                                .nearestEnclosingMethodScopeAround(currentScope_);
                        final IdentifierExpression self = new IdentifierExpression("this", type,
                                nearestEnclosingMethodScope, lineNumber);
                        self.setResultIsConsumed(true);
                        retval = new QualifiedIdentifierExpression(self, idName, type);
                    } else {
                        errorHook6p2(ErrorIncidenceType.Fatal, lineNumber, "Symbol `", idName,
                                "' is not public and so is not accessible to `", associatedDeclaration.name(), "'.",
                                "");
                    }
                } else {
                    final ClassDeclaration nextBaseClassDeclaration = baseClassDeclaration.baseClassDeclaration();
                    if (null == nextBaseClassDeclaration) {
                        errorHook5p2(ErrorIncidenceType.Fatal, lineNumber, "Symbol `", idName, "' cannot be found.",
                                "");
                    } else {
                        // Recur
                        retval = this.lookToBaseClassForHelp(idName, lineNumber,
                                nextBaseClassDeclaration.enclosedScope());
                    }
                }
            }
        }
        return retval;
    }

    @Override
    protected void updateTypesAccordingToPass(final /*Class*/Type type, final List<String> typeNameList) {
        // This little ditty is mainly for Pass2. It updates the template
        // instantiation information of classes like List<Foobar> where Foobar
        // is a forward type reference. Normally, template stuff can't take
        // advantage of type information again until Pass4 when templates
        // are fully instantiated. This is a bit of a cheat that at least
        // makes the template parameter mappings current, so that the appearance
        // of a template-typed script parameter will match properly on
        // lookup. It helps avoid some false warnings about type mismatches
        // when in fact there are none. Perhaps a rare occurrence (forward
        // references, templates, etc.) but nonetheless part of what we must
        // deal with....

        final StaticScope templateScope = null != type ? type.enclosedScope() : null;
        final TemplateInstantiationInfo currentTemplateInstantiationInfo = null != templateScope
                ? templateScope.templateInstantiationInfo()
                : null;
        final TemplateDeclaration templateDeclaration = null != currentTemplateInstantiationInfo
                ? currentTemplateInstantiationInfo.templateDeclaration()
                : null;
        final TemplateInstantiationInfo newTemplateInstantiationInfo = null != type
                ? new TemplateInstantiationInfo(templateDeclaration, type.name())
                : null;

        if (null != currentTemplateInstantiationInfo) {
            newTemplateInstantiationInfo.setClassType(currentTemplateInstantiationInfo.classType());

            for (final String typeName : typeNameList) {
                final Type templateParameterType = currentScope_.lookupTypeDeclarationRecursive(typeName);
                newTemplateInstantiationInfo.add(templateParameterType);
            }

            templateScope.resetTemplateInstationInfo(newTemplateInstantiationInfo);
        }
    }

    @Override
    protected ClassDeclaration lookupOrCreateClassDeclaration(final String name,
            final ClassDeclaration rawBaseClass, final ClassType baseType, final int lineNumber) {
        final ClassDeclaration newClass = currentScope_.lookupClassDeclarationRecursive(name);
        final Type rawClass = newClass.type();
        assert rawClass instanceof ClassType;
        final ClassType classType = (ClassType) rawClass;
        classType.updateBaseType(baseType);
        return newClass;
    }

    @Override
    protected void declareTypeSuitableToPass(final StaticScope scope, final Type decl) {
        /* Nothing */
    }

    @Override
    protected void declareObjectSuitableToPass(final StaticScope scope, final ObjectDeclaration objDecl) {
        if (scope.hasDeclarationsThatAreLostBetweenPasses()) {
            // e.g., a FOR Loop or a Block
            scope.declareObject(objDecl, this);
        } else {
            // most of the time...
            ; /* Nothing */
        }
    }

    @Override
    protected void declareFormalParametersSuitableToPass(final StaticScope scope, final ObjectDeclaration objDecl) {
        scope.reDeclareObject(objDecl);
    }

    @Override
    protected void addSignatureSuitableToPass(final InterfaceType interfaceType, final MethodSignature signature) {
        interfaceType.addSignature(signature);
    }

    @Override
    protected void addInterfaceTypeSuitableToPass(final ClassOrContextType classOrContextType,
            final InterfaceType interfaceType) {
        classOrContextType.addInterfaceType(interfaceType);
    }

    @Override
    protected void implementsCheck(final ClassOrContextDeclaration newDeclaration, final int lineNumber) {
        newDeclaration.doIImplementImplementsList(this, lineNumber);
    }

    @Override
    protected void errorHook5p1(final ErrorIncidenceType errorType, final int i, final String s1, final String s2,
            final String s3, final String s4) {
        /* Nothing */
    }

    @Override
    protected void errorHook6p1(final ErrorIncidenceType errorType, final int i, final String s1, final String s2,
            final String s3, final String s4, final String s5, final String s6) {
        /* Nothing */
    }

    @Override
    public void errorHook5p2(final ErrorIncidenceType errorType, final int i, final String s1, final String s2,
            final String s3, final String s4) {
        ErrorLogger.error(errorType, i, s1, s2, s3, s4);
    }

    @Override
    public void errorHook6p2(final ErrorIncidenceType errorType, final int i, final String s1, final String s2,
            final String s3, final String s4, final String s5, final String s6) {
        ErrorLogger.error(errorType, i, s1, s2, s3, s4, s5, s6);
    }

    public void errorHook5p3(final ErrorIncidenceType errorType, final int i, final String s1, final String s2,
            final String s3, final String s4) {
        ; // p3 and beyond only
    }

    @Override
    protected void updateInitializationLists(final Expression initializationExpr, final ObjectDeclaration objDecl) {
        // It actually is right that one of these is an add and one is an insert...
        // Same version for pass 2, 3, and 4
        initializationExpressions_.add(initializationExpr);
        variablesToInitialize_.insertAtStart(objDecl);
    }

    @Override
    public ObjectDeclaration pass1InitialDeclarationCheck(final String name, final int lineNumber) {
        final ObjectDeclaration objDecl = currentScope_.lookupObjectDeclaration(name);
        // It's been declared, so multiple declarations aren't an error
        return objDecl;
    }

    @Override
    protected void reportMismatchesWith(final int lineNumber, final RoleType lhsType, final Type rhsType) {
        lhsType.reportMismatchesWith(lineNumber, rhsType);
    }

    @Override
    protected void checkForAssignmentViolatingConstness(final AssignmentExpression assignment,
            final Token ctxGetStart) {
        final MethodDeclaration enclosingMethod = super.methodWithinWhichIAmDeclared(currentScope_);
        if (null != enclosingMethod && enclosingMethod.isConst()) {
            final Expression assignee = assignment.lhs();
            checkLhsForAssignmentViolatingConstness(assignee, enclosingMethod, ctxGetStart);
        }
    }

    @Override
    protected void checkForIncrementOpViolatingConstness(final ArrayIndexExpressionUnaryOp expression,
            final Token ctxGetStart) {
        final MethodDeclaration enclosingMethod = super.methodWithinWhichIAmDeclared(currentScope_);
        if (null != enclosingMethod && enclosingMethod.isConst()) {
            // We can have no idea where the array base is "pointing," so we have
            // to deny such expressions
            errorHook6p2(ErrorIncidenceType.Fatal, ctxGetStart.getLine(), "Modification of array ",
                    expression.getText(), " from within const method ", enclosingMethod.name(),
                    ", which violates the const modifier of the latter.", "");
        }
    }

    @Override
    protected void checkForIncrementOpViolatingIdentifierConstness(final UnaryopExpressionWithSideEffect id,
            final Token ctxGetStart) {
        final MethodDeclaration enclosingMethod = super.methodWithinWhichIAmDeclared(currentScope_);
        if (null != enclosingMethod && enclosingMethod.isConst()) {
            checkLhsForAssignmentViolatingConstness(id.lhs(), enclosingMethod, ctxGetStart);
        }
    }

    private void checkLhsForAssignmentViolatingConstness(final Expression assignee,
            final MethodDeclaration enclosingMethod, final Token ctxGetStart) {
        if (assignee instanceof IdentifierExpression) {
            final Declaration idDecl = currentScope_.lookupObjectDeclarationRecursiveWithinMethod(assignee.name());
            if (null == idDecl) {
                // Then it's not on the activation record
                errorHook5p2(ErrorIncidenceType.Fatal, ctxGetStart.getLine(),
                        "Assignment statement violates constness of declaration of ", enclosingMethod.name(), "",
                        "");
            }
        } else if (assignee instanceof QualifiedIdentifierExpression) {
            // We're assigning to something within "qualifier." That doesn't immediately
            // disqualify it - could be a locally created object.
            errorHook5p2(ErrorIncidenceType.Fatal, ctxGetStart.getLine(),
                    "Assignment statement modifies a member object that could be shared; ",
                    "that violates the constness of ", enclosingMethod.name(), "");
        } else if (assignee instanceof QualifiedClassMemberExpression) {
            // Certainly off-limits
            errorHook5p2(ErrorIncidenceType.Warning, ctxGetStart.getLine(),
                    "WARNING: Assignment statement modifies a class member that could be shared; ",
                    "that violates the constness of ", enclosingMethod.name(), "");
        } else if (assignee instanceof ArrayExpression) {
            // This is just an ArrayBase. In itself not a problem if it
            // is on the activation record of the method
            final ArrayExpression arrayExpression = (ArrayExpression) assignee;
            checkLhsForAssignmentViolatingConstness(arrayExpression.originalExpression(), enclosingMethod,
                    ctxGetStart);
        } else if (assignee instanceof ArrayIndexExpression) {
            // Can't know without full dataflow analysis
            errorHook5p2(ErrorIncidenceType.Warning, ctxGetStart.getLine(),
                    "WARNING: Assignment statement modifies an array member that could be shared; ",
                    "that violates the constness of `", enclosingMethod.name(), "'.");
        }
    }

    protected void checkForMessageSendViolatingConstness(final MethodSignature signature, final Token ctxGetStart) {
        final MethodDeclaration enclosingMethod = super.methodWithinWhichIAmDeclared(currentScope_);
        if (null != enclosingMethod && enclosingMethod.isConst()) {
            if (signature.hasConstModifier()) {
                ; // it's O.K. - this is a const method
            } else {
                errorHook6p2(ErrorIncidenceType.Fatal, ctxGetStart.getLine(), "Call of non-const method ",
                        signature.name(), " from within const method ", enclosingMethod.name(),
                        ", which violates the const modifier of the latter.", "");
            }
        }
    }

    // -------------------------------------------------------------------------------------------------------

    // WARNING. Tricky code here
    @Override
    public void declareObject(final StaticScope s, final ObjectDeclaration objdecl) {
        s.declareObject(objdecl, this);
    }

    @Override
    public void declareRoleOrStageProp(final StaticScope s, final RoleDeclaration roledecl, final int lineNumber) {
        s.declareRoleOrStageProp(roledecl); // probably redundant; done in pass 1
    }

    private void processDeclareRoleArrayAlias(final int lineNumber) {
        // Declare an actual object for the Role, if the Role is a RoleArray type
        if (currentRoleOrStageProp_.isArray()) {
            final String roleName = currentRoleOrStageProp_.type().getText();

            // Then declare an array base handle for it as well
            final String compoundName = roleName + "_$array";
            Type newType = currentScope_.lookupTypeDeclarationRecursive(compoundName);
            if (null == newType) {
                newType = new ArrayType(compoundName, currentRoleOrStageProp_.type());
                final ContextDeclaration contextDeclaration = currentRoleOrStageProp_.contextDeclaration();
                final StaticScope contextScope = contextDeclaration.type().enclosedScope();
                contextScope.declareType(newType);

                final ObjectDeclaration baseArrayObject = new ObjectDeclaration(compoundName, newType, lineNumber);
                contextScope.declareObject(baseArrayObject, this);
            }
        }
    }

    protected ActualArgumentList currentArgumentList() {
        return parsingData_.currentArgumentList();
    }
}