wyil.check.StaticVariableCheck 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 java.util.HashSet;
import java.util.Set;
import wycc.lang.Syntactic;
import wyil.lang.Compiler;
import wyil.lang.WyilFile;
import wyil.lang.WyilFile.Decl;
import wyil.lang.WyilFile.QualifiedName;
import wyil.util.AbstractConsumer;
import wyc.util.ErrorMessages;
import static wyil.lang.WyilFile.*;
/**
*
* Responsible for detecting cyclic static initialisers to ensure that every
* static variable can be given an initial value. This differs from other
* languages (e.g. Java) which don't detect static initialisers. Instead, in
* such languages, static variables are given a _default_ value which is the
* initial assignment used to resolve cyclic accesses. In Whiley, this approach
* does not work because no default can be safely determined in the presence of
* arbitrary type invariants. Furthermore, the approach taken in a language like
* Java is not sensible. Static initialisers can be either _directly_ or
* _indirectly_ cyclic, as illustrating with the following two examples:
*
*
*
* int x = x + 1
* int y = z + 1
* int z = y
*
*
*
* Here, static variable x
is directly cyclic whereas
* y
and z
are indirectly cyclic. An important
* implication of eliminating static initialisers is that, for any given
* `WyilFile`, one can determine a _safe_ initialisation order.
*
*
* @author David J. Pearce
*
*/
public class StaticVariableCheck extends AbstractConsumer> implements Compiler.Check {
private boolean status = true;
@Override
public boolean check(WyilFile wf) {
visitModule(wf, null);
return status;
}
@Override
public void visitExternalUnit(Decl.Unit unit, Set accessed) {
// NOTE: we override this to prevent unnecessarily traversing units
}
@Override
public void visitStaticVariable(Decl.StaticVariable decl, Set accessed) {
Expr initialiser = decl.getInitialiser();
QualifiedName name = decl.getQualifiedName();
accessed = new HashSet<>();
visitExpression(initialiser, accessed);
if (accessed.contains(name)) {
// Indicates a cyclic static initialiser has been detected
syntaxError(initialiser, CYCLIC_STATIC_INITIALISER);
}
}
@Override
public void visitType(Decl.Type decl, Set accessed) {
// Don't need to visit other declarations
}
@Override
public void visitCallable(Decl.Callable decl, Set accessed) {
// Don't need to visit other declarations
}
@Override
public void visitStatement(Stmt stmt, Set accessed) {
// Don't need to visit statements at all
}
@Override
public void visitStaticVariableAccess(Expr.StaticVariableAccess expr, Set accessed) {
Decl.Link l = expr.getLink();
Decl.StaticVariable decl = l.getTarget();
QualifiedName name = decl.getQualifiedName();
if (!accessed.contains(name)) {
accessed.add(name);
visitExpression(decl.getInitialiser(), accessed);
}
}
@Override
public void visitType(Type type, Set accessed) {
// Don't need to visit types at all
}
private void syntaxError(Syntactic.Item e, int code, Syntactic.Item... context) {
status = false;
ErrorMessages.syntaxError(e, code, context);
}
}