Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/main/scala/viper/silver/ast/Program.scala
Original file line number Diff line number Diff line change
Expand Up @@ -484,6 +484,8 @@ case class Function(name: String, formalArgs: Seq[LocalVarDecl], typ: Type, pres

def isAbstract = body.isEmpty

lazy val isPure = pres.forall(_.isPure)

override def isValid : Boolean /* Option[Message] */ = this match {
case _ if (for (e <- pres ++ posts) yield e.contains[MagicWand]).contains(true) => false
case _ if (for (e <- body) yield e.contains[MagicWand]).contains(true) => false
Expand Down
108 changes: 43 additions & 65 deletions src/main/scala/viper/silver/parser/Resolver.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
package viper.silver.parser

import viper.silver.FastMessaging
import viper.silver.ast.utility.Visitor
import viper.silver.ast.{LabelledOld, MagicWandOp}

import scala.collection.mutable
Expand Down Expand Up @@ -850,13 +849,17 @@ case class NameAnalyser() {

val scopeStack = mutable.Stack[PScope]()

var globalDeclarationsFound = false
Comment thread
marcoeilers marked this conversation as resolved.
Outdated

val nodeDownNameCollectorVisitor = new PartialFunction[PNode,Unit] {
def apply(n:PNode) = {
if (n == target.orNull)
namesInScope ++= getCurrentMap.map(_._1)
n match {
case d: PDeclaration =>
getMap(d).get(d.idndef.name) match {
case Some(_: PMember) if globalDeclarationsFound =>
// expected, do nothing.
case Some(e: PDeclaration) =>
messages ++= FastMessaging.message(e.idndef, "Duplicate identifier `" + e.idndef.name + "' at " + e.idndef.pos._1 + " and at " + d.idndef.pos._1)
case Some(_: PErrorEntity) =>
Expand All @@ -870,6 +873,22 @@ case class NameAnalyser() {
getMap(d).put(d.idndef.name, d)
}
}
case i@PIdnUse(name) =>
// look up in both maps (if we are not in a method currently, we look in the same map twice, but that is ok)
getCurrentMap.getOrElse(name, globalDeclarationMap.getOrElse(name, PUnknownEntity())) match {
case PUnknownEntity() =>
// domain types can also be type variables, which need not be declared
// goto and state labels may exist out of scope (but must exist in method, this is checked in final AST in checkIdentifiers)
if (i.parent.isDefined) {
val parent = i.parent.get
if (!parent.isInstanceOf[PDomainType] && !parent.isInstanceOf[PGoto] &&
!(parent.isInstanceOf[PLabelledOld] && i == parent.asInstanceOf[PLabelledOld].label) &&
!(name == LabelledOld.LhsOldLabel && parent.isInstanceOf[PLabelledOld])) {
messages ++= FastMessaging.message(i, s"identifier $name not defined.")
}
}
case _ =>
}
case _ =>
}

Expand All @@ -892,6 +911,7 @@ case class NameAnalyser() {
n match {
case _: PDeclaration => true
case _: PScope => true
case _: PIdnUse => true
case _ => target.isDefined
}
}
Expand All @@ -913,74 +933,32 @@ case class NameAnalyser() {
}
}

def containsSubnodeBefore(container: PNode, toFind: PNode, before: PNode) : Boolean = {
var beforeFound = false
val pred = new PartialFunction[PNode, PNode] {
def isDefinedAt(node: PNode): Boolean = {
if (!beforeFound){
if (node eq before){
beforeFound = true
}
}
(node eq toFind) && node != container && !beforeFound
n match {
case prog: PProgram =>
// find all global names first
for (d <- prog.domains) {
nodeDownNameCollectorVisitor(d)
d.funcs.foreach(f => {nodeDownNameCollectorVisitor(f); nodeUpNameCollectorVisitor(f)})
nodeUpNameCollectorVisitor(d)
}
prog.fields.foreach(f => f.visit(nodeDownNameCollectorVisitor,nodeUpNameCollectorVisitor))
prog.functions.foreach(f => {nodeDownNameCollectorVisitor(f); nodeUpNameCollectorVisitor(f)})
prog.predicates.foreach(f => {nodeDownNameCollectorVisitor(f); nodeUpNameCollectorVisitor(f)})
prog.methods.foreach(m => {nodeDownNameCollectorVisitor(m); nodeUpNameCollectorVisitor(m)})
prog.extensions.foreach(e => e.visit(nodeDownNameCollectorVisitor,nodeUpNameCollectorVisitor))

globalDeclarationsFound = true
// now completely walk through all axioms, functions, predicates, and methods
prog.domains.foreach(d => d.visit(nodeDownNameCollectorVisitor,nodeUpNameCollectorVisitor))
prog.functions.foreach(f => f.visit(nodeDownNameCollectorVisitor,nodeUpNameCollectorVisitor))
prog.predicates.foreach(f => f.visit(nodeDownNameCollectorVisitor,nodeUpNameCollectorVisitor))
prog.methods.foreach(m => m.visit(nodeDownNameCollectorVisitor,nodeUpNameCollectorVisitor))

def apply(node: PNode) = node
}
Visitor.existsDefined(container, Nodes.subnodes)(pred)
}

def getContainingMethod(node : PNode) : Option[PMethod] = {
node match {
case null => None
case method : PMethod => Some(method)
case nonMethod =>
nonMethod.parent match {
case Some(parentNode) => getContainingMethod(parentNode)
case None => None
}
}
case _ =>
// find all declarations
n.visit(nodeDownNameCollectorVisitor,nodeUpNameCollectorVisitor)
}

// find all declarations
n.visit(nodeDownNameCollectorVisitor,nodeUpNameCollectorVisitor)
clearUniversalDeclarationsMap()
/* Check all identifier uses. */
n.visit({
case m: PScope =>
scopeStack.push(curMember)
curMember = m
case i@PIdnUse(name) =>
// look up in both maps (if we are not in a method currently, we look in the same map twice, but that is ok)
getCurrentMap.getOrElse(name, globalDeclarationMap.getOrElse(name, PUnknownEntity())) match {
case PUnknownEntity() =>
// domain types can also be type variables, which need not be declared
// goto and state labels may exist out of scope (but must exist in method, this is checked in final AST in checkIdentifiers)
if (i.parent.isDefined) {
val parent = i.parent.get
if (!parent.isInstanceOf[PDomainType] && !parent.isInstanceOf[PGoto] &&
!(parent.isInstanceOf[PLabelledOld] && i==parent.asInstanceOf[PLabelledOld].label) &&
!(name == LabelledOld.LhsOldLabel && parent.isInstanceOf[PLabelledOld])) {
messages ++= FastMessaging.message(i, s"identifier $name not defined.")
}
}
case localVar : PLocalVarDecl =>
getContainingMethod(localVar) match {
case Some(PMethod(_, _, _, _, _, Some(actualBody))) =>
// Variables must not be used before they are declared
if (containsSubnodeBefore(actualBody, i, localVar)){
messages ++= FastMessaging.message(i, s"local variable $name cannot be accessed before it is declared.")
}
case _ =>
}
case _ =>
}
case _ =>
}, {
case _: PScope =>
curMember = scopeStack.pop()
case _ =>
})
}

def run(p: PProgram): Boolean = {
Expand Down