All Downloads are FREE. Search and download functionalities are using the official Maven repository.

wyil.check.FunctionalCheck Maven / Gradle / Ivy

// Copyright 2011 The Whiley Project Developers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package wyil.check;

import wyil.lang.Compiler;
import wyil.lang.WyilFile;
import wyil.lang.WyilFile.Decl;
import wyil.util.AbstractConsumer;
import wyc.util.ErrorMessages;
import static wyil.lang.WyilFile.*;
import wycc.lang.Syntactic;

/**
 * 

* Responsible for checking purity of statements and expressions in specific * context. The concept of a pure or referentially-transparent function is * relatively well understood. For example, Wikipedia lists two requirements: *

* *
    *
  1. The function always evaluates the same result value given the same * argument value(s). The function result value cannot depend on any hidden * information or state that may change while program execution proceeds or * between different executions of the program, nor can it depend on any * external input from I/O devices (usually—see below).
  2. *
  3. Evaluation of the result does not cause any semantically observable side * effect or output, such as mutation of mutable objects or output to I/O * devices.
  4. *
* *

* The purpose of the functional check is to ensure that the rules regarding * pure functions and related concepts are properly adhered to. There are two * specific contexts of importance here: *

* *
    *
  • Function Context. This corresponds to all statements and * expressions contained within a function. This is directly * comparable to the above statements regarding pure functions.
  • *
  • Functional Context. This corresponds to an expression in a context * which should be functionally pure. This includes all requires, * ensures, where clause and the conditions for * assert or assume statements. In addition, * constant expressions are also considered functional contexts. There * are used, for example, for Constant expressions are used, for example, for * static variable initialisers.
  • *
* *

* The rules enforced by this check are: (1) No expression or statement in a * function context can dereference a variable, invoke a method, * allocate objects via new or access a static variable; (2) * No expression in a functional context can invoke a method or * allocate objects via new. *

* * @author David J. Pearce * */ public class FunctionalCheck extends AbstractConsumer implements Compiler.Check { private boolean status = true; @Override public boolean check(WyilFile file) { visitModule(file, null); return status; } public enum Context { /** * Represents the body of a function, including specification elements. */ PURE, /** * Represents the body of a type invariant, property or function loop invariant. */ ONESTATE, /** * Represents the body of a variant. */ TWOSTATE, /** * Represents the body of a method precondition, loop invariant or * assumption/assertion. */ ONESTATE_IMPURE, /** * Represents the body of a method postcondition. */ TWOSTATE_IMPURE, /** * Represents the body of a method. */ IMPURE; /** * Determine whether we can call a method from this context, or not. * * @return */ public boolean canCallMethod() { return this == IMPURE; } /** * Determine whether we can call a variant from this context, or not. * * @return */ public boolean canCallVariant() { return this == TWOSTATE || this == TWOSTATE_IMPURE; } /** * Determine whether heap allocations are permitted in this context or not. * * @return */ public boolean canHeapAllocate() { return this == IMPURE; } /** * Determine whether can access (non-final) static variable in this context or * not. * * @return */ public boolean canAccessStatic() { return this == IMPURE || this == ONESTATE_IMPURE || this == TWOSTATE_IMPURE; } } @Override public void visitExternalUnit(Decl.Unit unit, Context data) { // NOTE: we override this to prevent unnecessarily traversing units } @Override public void visitType(Decl.Type decl, Context data) { visitVariable(decl.getVariableDeclaration(), data); visitExpressions(decl.getInvariant(), Context.ONESTATE); } @Override public void visitProperty(Decl.Property decl, Context data) { visitExpressions(decl.getRequires(), Context.PURE); visitStatement(decl.getBody(), Context.ONESTATE); } @Override public void visitVariant(Decl.Variant decl, Context data) { visitExpressions(decl.getInvariant(), Context.TWOSTATE); } @Override public void visitFunction(Decl.Function decl, Context data) { visitVariables(decl.getParameters(), Context.PURE); visitVariables(decl.getReturns(), Context.PURE); visitExpressions(decl.getRequires(), Context.PURE); visitExpressions(decl.getEnsures(), Context.PURE); visitStatement(decl.getBody(), Context.PURE); } @Override public void visitMethod(Decl.Method decl, Context data) { // No need to visit variables here as no restrictions imposed. visitExpressions(decl.getRequires(), Context.ONESTATE_IMPURE); visitExpressions(decl.getEnsures(), Context.TWOSTATE_IMPURE); visitStatement(decl.getBody(), Context.IMPURE); } @Override public void visitStaticVariable(Decl.StaticVariable decl, Context data) { // FIXME: should also prohibit invocation of pure functions in this context? visitExpression(decl.getInitialiser(), Context.PURE); } @Override public void visitAssert(Stmt.Assert stmt, Context context) { visitExpression(stmt.getCondition(), toFunctional(context)); } @Override public void visitAssume(Stmt.Assume stmt, Context context) { visitExpression(stmt.getCondition(), toFunctional(context)); } @Override public void visitDereference(Expr.Dereference expr, Context context) { if (context == Context.PURE) { syntaxError(expr, REFERENCE_ACCESS_NOT_PERMITTED); } super.visitDereference(expr, context); } @Override public void visitFieldDereference(Expr.FieldDereference expr, Context context) { if (context == Context.PURE) { syntaxError(expr, REFERENCE_ACCESS_NOT_PERMITTED); } super.visitFieldDereference(expr, context); } @Override public void visitDoWhile(Stmt.DoWhile stmt, Context context) { visitStatement(stmt.getBody(), context); visitExpression(stmt.getCondition(), context); visitExpressions(stmt.getInvariant(), toFunctional(context)); } @Override public void visitFor(Stmt.For stmt, Context context) { visitExpression(stmt.getVariable().getInitialiser(), context); visitStatement(stmt.getBody(), context); visitExpressions(stmt.getInvariant(), toFunctional(context)); } @Override public void visitWhile(Stmt.While stmt, Context context) { visitStatement(stmt.getBody(), context); visitExpression(stmt.getCondition(), context); visitExpressions(stmt.getInvariant(), toFunctional(context)); } @Override public void visitInvoke(Expr.Invoke expr, Context context) { // Check whether invoking an impure method in a pure context Decl.Link name = expr.getLink(); if (!context.canCallMethod() && name.isResolved() && name.getTarget() instanceof Decl.Method) { syntaxError(expr, METHODCALL_NOT_PERMITTED); } else if (!context.canCallVariant() && name.isResolved() && name.getTarget() instanceof Decl.Variant) { syntaxError(expr, VARIANTCALL_NOT_PERMITTED); } super.visitInvoke(expr, context); } @Override public void visitIndirectInvoke(Expr.IndirectInvoke expr, Context context) { // Check whether invoking an impure method in a pure context if (!context.canCallMethod() && isMethodType(expr.getSource().getType())) { syntaxError(expr, METHODCALL_NOT_PERMITTED); } super.visitIndirectInvoke(expr, context); } @Override public void visitExistentialQuantifier(Expr.ExistentialQuantifier expr, Context context) { for(Decl.StaticVariable v : expr.getParameters()) { visitExpression(v.getInitialiser(), context); } visitExpression(expr.getOperand(), context); } @Override public void visitUniversalQuantifier(Expr.UniversalQuantifier expr, Context context) { for(Decl.StaticVariable v : expr.getParameters()) { visitExpression(v.getInitialiser(), context); } visitExpression(expr.getOperand(), context); } @Override public void visitLambda(Decl.Lambda expr, Context context) { if(expr.getType() instanceof Type.Method) { super.visitLambda(expr, Context.IMPURE); } else { super.visitLambda(expr, Context.PURE); } } @Override public void visitNew(Expr.New expr, Context context) { if (!context.canHeapAllocate()) { syntaxError(expr, ALLOCATION_NOT_PERMITTED); } super.visitNew(expr, context); } @Override public void visitStaticVariableAccess(Expr.StaticVariableAccess expr, Context context) { Decl.StaticVariable v = expr.getLink().getTarget(); // Prohibit static variable accesses in pure contexts. if (!context.canAccessStatic() && v.getModifiers().match(Modifier.Final.class) == null) { syntaxError(expr, STATIC_ACCESS_NOT_PERMITTED); } } @Override public void visitType(Type type, Context context) { // NOTE: don't traverse types as this is unnecessary. Even in a pure context, // seemingly impure types (e.g. references and methods) can still be used // safely. } public boolean isMethodType(Type type) { if (type instanceof Type.Method) { return true; } else if (type instanceof Type.Nominal) { Type.Nominal n = (Type.Nominal) type; Decl.Link l = n.getLink(); if(l.isResolved()) { return isMethodType(l.getTarget().getType()); } else { // NOTE: this is handle error recovery for situations where name resolution // failed on the nominal type. return false; } } else { return false; } } /** * Upgrade a given context to a functional or pure context. If the context * already was PURE, then it remains as PURE (this is the highest level). * Otherwise, it is FUNCTIONAL. * * @param context * @return */ public Context toFunctional(Context context) { if (context == Context.PURE) { return context; } else { return Context.ONESTATE_IMPURE; } } private void syntaxError(Syntactic.Item e, int code, Syntactic.Item... context) { status = false; ErrorMessages.syntaxError(e, code, context); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy