Skip to content

Commit 0e78f7c

Browse files
committed
Allow adt constructors to have associated axiom
1 parent 5295393 commit 0e78f7c

File tree

6 files changed

+160
-58
lines changed

6 files changed

+160
-58
lines changed

src/main/scala/viper/silver/parser/ParseAst.scala

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,11 @@ sealed trait PIdnUseName[T <: PDeclarationInner] extends PIdnUse {
268268
/** Any `PNode` which should be ignored (as well as it's children) by the `NameAnalyser`. */
269269
trait PNameAnalyserOpaque extends PNode
270270

271+
trait PNameAnalyserCustom extends PNode {
272+
def nameDown(map: NameAnalyserCtxt): Unit
273+
def nameUp(map: NameAnalyserCtxt): Unit
274+
}
275+
271276
case class PIdnRef[T <: PDeclarationInner](name: String)(val pos: (Position, Position))(implicit val ctag: scala.reflect.ClassTag[T]) extends PIdnUseName[T] {
272277
override def rename(newName: String): PIdnUse = PIdnRef(newName)(pos)
273278
/** Changes the type of declaration which is referenced, preserves all previously added `decls` but discards `filters`. */
@@ -1691,7 +1696,11 @@ case class PDomainFunction(annotations: Seq[PAnnotation], unique: Option[PKw.Uni
16911696
override def endLinebreak = false
16921697
}
16931698

1694-
case class PAxiom(annotations: Seq[PAnnotation], axiom: PKw.Axiom, idndef: Option[PIdnDef], exp: PBracedExp)(val pos: (Position, Position)) extends PDomainMember
1699+
/** Declares that all children expressions must always be well-defined. This is
1700+
* enforced during type-checking. Used e.g. for axioms. */
1701+
trait PAlwaysWellDefined
1702+
1703+
case class PAxiom(annotations: Seq[PAnnotation], axiom: PKw.Axiom, idndef: Option[PIdnDef], exp: PBracedExp)(val pos: (Position, Position)) extends PDomainMember with PAlwaysWellDefined
16951704

16961705
case class PDomainInterpretation(name: PRawString, c: PSym.Colon, lit: PStringLiteral)(val pos: (Position, Position)) extends PNode
16971706
case class PDomainInterpretations(k: PReserved[PKeywordLang], m: PDelimited.Comma[PSym.Paren, PDomainInterpretation])(val pos: (Position, Position)) extends PNode {

src/main/scala/viper/silver/parser/Resolver.scala

Lines changed: 70 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -699,7 +699,7 @@ case class TypeChecker(names: NameAnalyser) {
699699
check(fd.typ)
700700
fd.formalArgs foreach (a => check(a.typ))
701701
}
702-
if (pfa.isDescendant[PAxiom] && pfn.pres.toSeq.exists(pre => pre.k.rs == Requires)) {
702+
if (pfa.isDescendant[PAlwaysWellDefined] && pfn.pres.toSeq.exists(pre => pre.k.rs == Requires)) {
703703
// A domain axiom, which must always be well-defined, is calling a function that has at least
704704
// one real precondition (i.e., not just a requires clause or something similar that's
705705
// temporarily represented as a precondition), which means that the call may not always be
@@ -941,6 +941,13 @@ case class DeclarationMap(
941941
def keys = decls.keys
942942
}
943943

944+
// Used by `PNameAnalyserCustom`
945+
trait NameAnalyserCtxt {
946+
def getMap(): DeclarationMap
947+
def pushScope(s: PScope): Unit
948+
def popScope(): Unit
949+
}
950+
944951
/**
945952
* Resolves identifiers to their declaration. The important traits that relate to this are:
946953
* - `PDeclaration` marks a declaration of an identifier.
@@ -960,6 +967,7 @@ case class DeclarationMap(
960967
* - `PGlobalUniqueDeclaration`: marks a declaration as unique within the program.
961968
*
962969
* - `PNameAnalyserOpaque` marks a scope as opaque (should not be traversed by the name analyser).
970+
* - `PNameAnalyserCustom` marks a node which implements custom logic for name analysis.
963971
*/
964972
case class NameAnalyser() {
965973

@@ -992,41 +1000,79 @@ case class NameAnalyser() {
9921000

9931001
private val namesInScope = mutable.Set.empty[String]
9941002

995-
def check(g: PNode, target: Option[PNode], initialCurScope: PScope = null): Unit = {
996-
var curScope: PScope = initialCurScope
997-
def getMap(): DeclarationMap = Option(curScope).map(_.scopeId).map(localDeclarationMaps.get(_).get).getOrElse(globalDeclarationMap)
998-
1003+
case class NameAnalyserCtxt(names: NameAnalyser) extends viper.silver.parser.NameAnalyserCtxt {
1004+
var curScope: PScope = null
9991005
val scopeStack = mutable.Stack[PScope]()
10001006
var opaque = 0
10011007

1008+
def getMap(): DeclarationMap = Option(curScope).map(_.scopeId).map(names.localDeclarationMaps.get(_).get).getOrElse(names.globalDeclarationMap)
1009+
1010+
def pushScope(s: PScope): Unit = {
1011+
names.localDeclarationMaps.put(s.scopeId, DeclarationMap(isMember = s.isInstanceOf[PMember]))
1012+
scopeStack.push(curScope)
1013+
curScope = s
1014+
}
1015+
1016+
def popScope(): Unit = {
1017+
val popMap = localDeclarationMaps.get(curScope.scopeId).get
1018+
val newScope = scopeStack.pop()
1019+
curScope = newScope
1020+
1021+
val clashing = getMap().merge(popMap)
1022+
clashing.foreach { case (clashing, unique) =>
1023+
messages ++= FastMessaging.message(clashing.idndef, s"duplicate identifier `${clashing.idndef.name}` at ${clashing.idndef.pos._1} and at ${unique.idndef.pos._1}")
1024+
}
1025+
}
1026+
1027+
def finish(): Unit = {
1028+
// If we started from some inner scope, walk all the way back out to the whole program
1029+
// with a variation of nodeUpNameCollectorVisitor
1030+
while (curScope != null) {
1031+
val popMap = localDeclarationMaps.get(curScope.scopeId).get
1032+
curScope.getAncestor[PScope] match {
1033+
case Some(newScope) =>
1034+
curScope = newScope
1035+
case None =>
1036+
curScope = null
1037+
}
1038+
getMap().merge(popMap)
1039+
}
1040+
}
1041+
}
1042+
1043+
def check(g: PNode, target: Option[PNode], initialCurScope: PScope = null): Unit = {
1044+
val ctx: NameAnalyserCtxt = NameAnalyserCtxt(this)
1045+
ctx.curScope = initialCurScope
1046+
10021047
val nodeDownNameCollectorVisitor = new PartialFunction[PNode, Unit] {
10031048
def apply(n: PNode) = {
10041049
if (n == target.orNull)
1005-
namesInScope ++= getMap().keys
1050+
namesInScope ++= ctx.getMap().keys
10061051
n match {
10071052
// Opaque
10081053
case _: PNameAnalyserOpaque =>
1009-
opaque += 1
1010-
case _ if opaque > 0 =>
1054+
ctx.opaque += 1
1055+
case _ if ctx.opaque > 0 =>
1056+
// Custom
1057+
case c: PNameAnalyserCustom =>
1058+
c.nameDown(ctx)
10111059
// Regular
10121060
case d: PDeclaration =>
10131061
// Add to declaration map
1014-
val localDecls = getMap()
1062+
val localDecls = ctx.getMap()
10151063
localDecls.newDecl(d)
10161064
val clashing = localDecls.checkUnique(d, true)
10171065
if (clashing.isDefined)
10181066
messages ++= FastMessaging.message(d.idndef, s"duplicate identifier `${d.idndef.name}` at ${d.idndef.pos._1} and at ${clashing.get.idndef.pos._1}")
10191067
case i: PIdnUseName[_] if target.isEmpty =>
1020-
getMap().newRef(i)
1068+
ctx.getMap().newRef(i)
10211069
case _ =>
10221070
}
10231071

10241072
n match {
1025-
case _ if opaque > 0 =>
1026-
case s: PScope =>
1027-
localDeclarationMaps.put(s.scopeId, DeclarationMap(isMember = s.isInstanceOf[PMember]))
1028-
scopeStack.push(curScope)
1029-
curScope = s
1073+
case _ if ctx.opaque > 0 =>
1074+
case _: PNameAnalyserCustom =>
1075+
case s: PScope => ctx.pushScope(s)
10301076
case _ =>
10311077
}
10321078
}
@@ -1037,6 +1083,7 @@ case class NameAnalyser() {
10371083
case _: PScope => true
10381084
case _: PIdnUseName[_] => true
10391085
case _: PNameAnalyserOpaque => true
1086+
case _: PNameAnalyserCustom => true
10401087
case _ => target.isDefined
10411088
}
10421089
}
@@ -1047,18 +1094,13 @@ case class NameAnalyser() {
10471094
n match {
10481095
// Opaque
10491096
case _: PNameAnalyserOpaque =>
1050-
opaque -= 1
1051-
case _ if opaque > 0 =>
1097+
ctx.opaque -= 1
1098+
case _ if ctx.opaque > 0 =>
1099+
// Custom
1100+
case c: PNameAnalyserCustom =>
1101+
c.nameUp(ctx)
10521102
// Regular
1053-
case _: PScope =>
1054-
val popMap = localDeclarationMaps.get(curScope.scopeId).get
1055-
val newScope = scopeStack.pop()
1056-
curScope = newScope
1057-
1058-
val clashing = getMap().merge(popMap)
1059-
clashing.foreach { case (clashing, unique) =>
1060-
messages ++= FastMessaging.message(clashing.idndef, s"duplicate identifier `${clashing.idndef.name}` at ${clashing.idndef.pos._1} and at ${unique.idndef.pos._1}")
1061-
}
1103+
case _: PScope => ctx.popScope()
10621104
case _ =>
10631105
}
10641106
}
@@ -1078,18 +1120,8 @@ case class NameAnalyser() {
10781120
// If we started from some inner scope, walk all the way back out to the whole program
10791121
// with a variation of nodeUpNameCollectorVisitor
10801122
if (initialCurScope != null) {
1081-
assert(initialCurScope == curScope)
1082-
1083-
while (curScope != null) {
1084-
val popMap = localDeclarationMaps.get(curScope.scopeId).get
1085-
curScope.getAncestor[PScope] match {
1086-
case Some(newScope) =>
1087-
curScope = newScope
1088-
case None =>
1089-
curScope = null
1090-
}
1091-
getMap().merge(popMap)
1092-
}
1123+
assert(initialCurScope == ctx.curScope)
1124+
ctx.finish()
10931125
}
10941126
}
10951127

src/main/scala/viper/silver/plugin/standard/adt/AdtASTExtension.scala

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ case class Adt(name: String, constructors: Seq[AdtConstructor], typVars: Seq[Typ
6666
* @param typ the return type of the constructor
6767
* @param adtName the name corresponding of the corresponding ADT
6868
*/
69-
case class AdtConstructor(name: String, formalArgs: Seq[LocalVarDecl])
69+
case class AdtConstructor(name: String, formalArgs: Seq[LocalVarDecl], axiom: Option[Exp])
7070
(val pos: Position, val info: Info, val typ: AdtType, val adtName: String, val errT: ErrorTrafo)
7171
extends ExtensionMember {
7272

@@ -84,19 +84,20 @@ case class AdtConstructor(name: String, formalArgs: Seq[LocalVarDecl])
8484
if (!forceRewrite && this.children == children && pos.isEmpty)
8585
this
8686
else {
87-
assert(children.length == 2, s"AdtConstructor : expected length 2 but got ${children.length}")
87+
assert(children.length == 3, s"AdtConstructor : expected length 3 but got ${children.length}")
8888
val first = children.head.asInstanceOf[String]
8989
val second = children.tail.head.asInstanceOf[Seq[LocalVarDecl]]
90-
AdtConstructor(first, second)(this.pos, this.info, this.typ, this.adtName, this.errT).asInstanceOf[this.type]
90+
val third = children.tail.tail.head.asInstanceOf[Option[Exp]]
91+
AdtConstructor(first, second, third)(this.pos, this.info, this.typ, this.adtName, this.errT).asInstanceOf[this.type]
9192
}
9293

9394
}
9495
}
9596

9697
object AdtConstructor {
97-
def apply(adt: Adt, name: String, formalArgs: Seq[LocalVarDecl])
98+
def apply(adt: Adt, name: String, formalArgs: Seq[LocalVarDecl], axiom: Option[Exp])
9899
(pos: Position = NoPosition, info: Info = NoInfo, errT: ErrorTrafo = NoTrafos): AdtConstructor = {
99-
AdtConstructor(name, formalArgs)(pos, info, AdtType(adt, (adt.typVars zip adt.typVars).toMap), adt.name, errT)
100+
AdtConstructor(name, formalArgs, axiom)(pos, info, AdtType(adt, (adt.typVars zip adt.typVars).toMap), adt.name, errT)
100101
}
101102
}
102103

src/main/scala/viper/silver/plugin/standard/adt/AdtPASTExtension.scala

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ object PAdtFieldDecl {
108108
def apply(d: PIdnTypeBinding): PAdtFieldDecl = PAdtFieldDecl(d.idndef, d.c, d.typ)(d.pos)
109109
}
110110

111-
case class PAdtConstructor(annotations: Seq[PAnnotation], idndef: PIdnDef, args: PDelimited.Comma[PSym.Paren, PAdtFieldDecl])(val pos: (Position, Position)) extends PExtender with PNoSpecsFunction with PGlobalUniqueDeclaration with PAdtChild {
111+
case class PAdtConstructor(annotations: Seq[PAnnotation], idndef: PIdnDef, args: PDelimited.Comma[PSym.Paren, PAdtFieldDecl], axiom: Option[PAdtConstructorAxiom])(val pos: (Position, Position)) extends PExtender with PNoSpecsFunction with PGlobalUniqueDeclaration with PAdtChild {
112112
override def resultType: PType = adt.getAdtType
113113
def fieldDecls: Seq[PAdtFieldDecl] = this.args.inner.toSeq
114114
override def typecheck(t: TypeChecker, n: NameAnalyser): Option[Seq[String]] = {
@@ -119,14 +119,16 @@ case class PAdtConstructor(annotations: Seq[PAnnotation], idndef: PIdnDef, args:
119119
val decl = discClashes.head
120120
t.messages ++= FastMessaging.message(idndef, "corresponding adt discriminator identifier `" + decl.idndef.name + "` at " + idndef.pos._1 + " is shadowed at " + decl.idndef.pos._1)
121121
}
122+
axiom foreach (_.typecheck(t, n))
122123
None
123124
}
124125

125126
override def translateMemberSignature(t: Translator): AdtConstructor = {
126127
AdtConstructor(
127128
PAdt.findAdt(adt.idndef, t),
128129
idndef.name,
129-
formalArgs map (_.asInstanceOf[PAdtFieldDecl]) map { arg => LocalVarDecl(arg.idndef.name, t.ttyp(arg.typ))(t.liftPos(arg.idndef)) }
130+
formalArgs map (_.asInstanceOf[PAdtFieldDecl]) map { arg => LocalVarDecl(arg.idndef.name, t.ttyp(arg.typ))(t.liftPos(arg.idndef)) },
131+
axiom map (axiom => t.exp(axiom.e.inner))
130132
)(t.liftPos(this), Translator.toInfo(annotations, this))
131133
}
132134

@@ -150,6 +152,24 @@ object PAdtConstructor {
150152
def findAdtConstructor(id: PIdentifier, t: Translator): AdtConstructor = t.getMembers()(id.name).asInstanceOf[AdtConstructor]
151153
}
152154

155+
case class PAdtConstructorAxiom(axiom: PKw.Axiom, e: PGrouped.Paren[PExp])(val pos: (Position, Position)) extends PExtender with PDomainMember with PNameAnalyserCustom with PAlwaysWellDefined {
156+
override def nameDown(ctx: NameAnalyserCtxt): Unit = {
157+
ctx.pushScope(this)
158+
val map = ctx.getMap()
159+
val args = getAncestor[PAdtConstructor].get.args.inner.toSeq
160+
args.map(a => PLogicalVarDecl(a.idndef, a.c, a.typ)(a.pos)).foreach(map.newDecl)
161+
}
162+
override def nameUp(ctx: NameAnalyserCtxt): Unit = ctx.popScope()
163+
164+
// We extend `PDomainMember` to make this a `PScope`
165+
override def domain: PDomain = null
166+
167+
override def typecheck(t: TypeChecker, n: NameAnalyser): Option[Seq[String]] = {
168+
t.check(e.inner, TypeHelper.Bool)
169+
None
170+
}
171+
}
172+
153173
case class PAdtDeriving(k: PReserved[PDerivesKeyword.type], derivingInfos: PAdtSeq[PAdtDerivingInfo])(val pos: (Position, Position)) extends PExtender with PAdtChild {
154174
override def typecheck(t: TypeChecker, n: NameAnalyser): Option[Seq[String]] = {
155175
derivingInfos.inner foreach (_.typecheck(t, n))

src/main/scala/viper/silver/plugin/standard/adt/AdtPlugin.scala

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ class AdtPlugin(@unused reporter: viper.silver.reporter.Reporter,
2020
config: viper.silver.frontend.SilFrontendConfig,
2121
fp: FastParser) extends SilverPlugin with ParserPluginTemplate {
2222

23-
import fp.{annotation, argList, idnTypeBinding, idndef, idnref, semiSeparated, typ, typeList, domainTypeVarDecl, ParserExtension, lineCol, _file}
23+
import fp.{annotation, argList, idnTypeBinding, idndef, idnref, parenthesizedExp, semiSeparated, typ, typeList, domainTypeVarDecl, ParserExtension, lineCol, _file}
2424
import FastParserCompanion.{ExtendedParsing, LeadingWhitespace, PositionParsing, reservedKw, reservedSym, whitespace}
2525

2626
/**
@@ -91,7 +91,9 @@ class AdtPlugin(@unused reporter: viper.silver.reporter.Reporter,
9191

9292
def adtConstructorDecls[$: P]: P[PAdtSeq[PAdtConstructor]] = P(semiSeparated(adtConstructorDecl).braces.map(PAdtSeq.apply _)).pos
9393

94-
def adtConstructorDecl[$: P]: P[PAdtConstructor] = P((annotation.rep ~ idndef ~ argList(idnTypeBinding.map(PAdtFieldDecl(_)))) map (PAdtConstructor.apply _).tupled).pos
94+
def adtConstructorDecl[$: P]: P[PAdtConstructor] = P((annotation.rep ~ idndef ~ argList(idnTypeBinding.map(PAdtFieldDecl(_))) ~ adtConstructorAxiom.?) map (PAdtConstructor.apply _).tupled).pos
95+
96+
def adtConstructorAxiom[$: P]: P[PAdtConstructorAxiom] = P((P(PKw.Axiom) ~ parenthesizedExp) map (PAdtConstructorAxiom.apply _).tupled).pos
9597

9698
override def beforeResolve(input: PProgram): PProgram = {
9799
if (deactivated) {

0 commit comments

Comments
 (0)