Skip to content

Commit 73ce0e7

Browse files
authored
Merge pull request #574 from amaissen/master
Add support for ADTs in Viper
2 parents ba9ea5a + 81b2a02 commit 73ce0e7

49 files changed

Lines changed: 2872 additions & 1 deletion

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// This Source Code Form is subject to the terms of the Mozilla Public
2+
// License, v. 2.0. If a copy of the MPL was not distributed with this
3+
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
4+
//
5+
// Copyright (c) 2011-2022 ETH Zurich.
6+
7+
domain ContainsDomain[A,B] {
8+
function contains(a: A, b: B): Bool
9+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
import <adt/contains.vpr>

src/main/scala/viper/silver/frontend/SilFrontEndConfig.scala

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,13 @@ abstract class SilFrontendConfig(args: Seq[String], private var projectName: Str
111111
hidden = true
112112
)
113113

114+
val adtPlugin = opt[Boolean]("disableAdtPlugin",
115+
descr = "Disable the ADT plugin, which adds support for ADTs as a built-in type.",
116+
default = Some(false),
117+
noshort = true,
118+
hidden = true
119+
)
120+
114121
val assumeInjectivityOnInhale = opt[Boolean]("assumeInjectivityOnInhale",
115122
descr = "Assumes injectivity of the receiver expression when inhaling quantified permissions, instead of checking it.",
116123
default = Some(false),

src/main/scala/viper/silver/frontend/SilFrontend.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ trait SilFrontend extends DefaultFrontend {
8181
* All default plugins can be excluded from the plugins by providing the --disableDefaultPlugins flag
8282
*/
8383
private val defaultPlugins: Seq[String] = Seq(
84+
"viper.silver.plugin.standard.adt.AdtPlugin",
8485
"viper.silver.plugin.standard.termination.TerminationPlugin",
8586
"viper.silver.plugin.standard.predicateinstance.PredicateInstancePlugin"
8687
)
Lines changed: 310 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,310 @@
1+
// This Source Code Form is subject to the terms of the Mozilla Public
2+
// License, v. 2.0. If a copy of the MPL was not distributed with this
3+
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
4+
//
5+
// Copyright (c) 2011-2022 ETH Zurich.
6+
7+
package viper.silver.plugin.standard.adt
8+
9+
import viper.silver.ast._
10+
import viper.silver.ast.pretty.FastPrettyPrinter.{ContOps, braces, brackets, char, defaultIndent, line, nest, nil, parens, show, showType, showVars, space, ssep, text}
11+
import viper.silver.ast.pretty.PrettyPrintPrimitives
12+
import viper.silver.ast.utility.Consistency
13+
import viper.silver.verifier.{ConsistencyError, Failure, VerificationResult}
14+
15+
/**
16+
* This class represents a user-defined ADT.
17+
*
18+
* @param name name of the ADT
19+
* @param constructors sequence of corresponding constructors
20+
* @param typVars sequence of type variables (generics)
21+
* @param derivingInfo a map that maps identifiers of derivable functions to a tuple containing an optional type param and a set of excluded constructor argument identifiers
22+
*/
23+
case class Adt(name: String, constructors: Seq[AdtConstructor], typVars: Seq[TypeVar] = Nil, derivingInfo: Map[String, (Option[Type], Set[String])] = Map.empty)
24+
(val pos: Position = NoPosition, val info: Info = NoInfo, val errT: ErrorTrafo = NoTrafos) extends ExtensionMember {
25+
val scopedDecls: Seq[Declaration] = Seq()
26+
27+
override def extensionSubnodes: Seq[Node] = constructors ++ typVars ++ derivingInfo.map(_._2._1).collect { case Some(v) => v }
28+
29+
override def prettyPrint: PrettyPrintPrimitives#Cont = {
30+
31+
def showDerivingInfo(di: (String, (Option[Type], Set[String]))): PrettyPrintPrimitives#Cont = {
32+
val (func, (param, blocklist)) = di
33+
text(func) <> (if (param.isEmpty) nil else text("[") <> showType(param.get) <> space <> "]") <>
34+
space <> (if (blocklist.isEmpty) nil else text("without") <> space <> ssep(blocklist.toSeq map text, char(',') <> space))
35+
}
36+
37+
text("adt") <+> name <>
38+
(if (typVars.isEmpty) nil else text("[") <> ssep(typVars map show, char(',') <> space) <> "]") <+>
39+
braces(nest(defaultIndent,
40+
line <> line <>
41+
ssep(constructors map show, line <> line)
42+
) <> line) <+>
43+
(if (derivingInfo.isEmpty) nil else text("derives") <+>
44+
braces(nest(defaultIndent,
45+
line <> line <>
46+
ssep(derivingInfo.toSeq map showDerivingInfo, line <> line)
47+
) <> line)
48+
)
49+
}
50+
}
51+
52+
/**
53+
* This class represents an ADT constructor. See also the companion object below, which allows passing a
54+
* Adt - this should be used in general for creation (so that typ is guaranteed to
55+
* be set correctly)
56+
*
57+
* @param name name of the ADT constructor
58+
* @param formalArgs sequence of arguments of the constructor
59+
* @param typ the return type of the constructor
60+
* @param adtName the name corresponding of the corresponding ADT
61+
*/
62+
case class AdtConstructor(name: String, formalArgs: Seq[LocalVarDecl])
63+
(val pos: Position, val info: Info, val typ: AdtType, val adtName: String, val errT: ErrorTrafo)
64+
extends ExtensionMember {
65+
66+
val scopedDecls: Seq[Declaration] = formalArgs
67+
68+
override def getMetadata: Seq[Any] = {
69+
Seq(pos, info, errT)
70+
}
71+
72+
override def extensionSubnodes: Seq[Node] = formalArgs
73+
74+
override def prettyPrint: PrettyPrintPrimitives#Cont = text(name) <> parens(showVars(formalArgs))
75+
76+
override def withChildren(children: Seq[Any], pos: Option[(Position, Position)], forceRewrite: Boolean): this.type = {
77+
if (!forceRewrite && this.children == children && pos.isEmpty)
78+
this
79+
else {
80+
assert(children.length == 2, s"AdtConstructor : expected length 2 but got ${children.length}")
81+
val first = children.head.asInstanceOf[String]
82+
val second = children.tail.head.asInstanceOf[Seq[LocalVarDecl]]
83+
AdtConstructor(first, second)(this.pos, this.info, this.typ, this.adtName, this.errT).asInstanceOf[this.type]
84+
}
85+
86+
}
87+
}
88+
89+
object AdtConstructor {
90+
def apply(adt: Adt, name: String, formalArgs: Seq[LocalVarDecl])
91+
(pos: Position = NoPosition, info: Info = NoInfo, errT: ErrorTrafo = NoTrafos): AdtConstructor = {
92+
AdtConstructor(name, formalArgs)(pos, info, AdtType(adt, (adt.typVars zip adt.typVars).toMap), adt.name, errT)
93+
}
94+
}
95+
96+
/**
97+
* This class represents an user-defined ADT type. See also the companion object below, which allows passing a
98+
* Adt - this should be used in general for creation (so that typeParameters is guaranteed to
99+
* be set correctly)
100+
*
101+
* @param adtName The name of the underlying adt.
102+
* @param partialTypVarsMap Maps type parameters to (possibly concrete) types. May not map all
103+
* type parameters, may even be empty.
104+
* @param typeParameters The type variables of the ADT type.
105+
*/
106+
case class AdtType(adtName: String, partialTypVarsMap: Map[TypeVar, Type])
107+
(val typeParameters: Seq[TypeVar]) extends ExtensionType {
108+
109+
override lazy val check: Seq[ConsistencyError] = if (!(typeParameters.toSet == typVarsMap.keys.toSet)) {
110+
Seq(ConsistencyError(s"${typeParameters.toSet} doesn't equal ${typVarsMap.keys.toSet}", NoPosition))
111+
} else Seq()
112+
113+
/**
114+
* Map each type parameter `A` to `partialTypVarsMap(A)`, if defined, otherwise to `A` itself.
115+
* `typVarsMap` thus contains a mapping for each type parameter.
116+
*/
117+
val typVarsMap: Map[TypeVar, Type] =
118+
typeParameters.map(tp => tp -> partialTypVarsMap.getOrElse(tp, tp)).to(implicitly)
119+
120+
/**
121+
* Takes a mapping of type variables to types and substitutes all
122+
* occurrences of those type variables with the corresponding type.
123+
*/
124+
override def substitute(newTypVarsMap: Map[TypeVar, Type]): Type = {
125+
assert(typVarsMap.keys.toSet equals this.typeParameters.toSet)
126+
val newTypeMap = typVarsMap.map(kv => kv._1 -> kv._2.substitute(newTypVarsMap))
127+
AdtType(adtName, newTypeMap)(typeParameters)
128+
}
129+
130+
/** Is this a concrete type (i.e. no uninstantiated type variables)? */
131+
override def isConcrete: Boolean = typVarsMap.values.forall(_.isConcrete)
132+
133+
override def prettyPrint: PrettyPrintPrimitives#Cont = {
134+
val typArgs = typeParameters map (t => show(typVarsMap.getOrElse(t, t)))
135+
text(adtName) <> (if (typArgs.isEmpty) nil else brackets(ssep(typArgs, char(',') <> space)))
136+
}
137+
138+
override def withChildren(children: Seq[Any], pos: Option[(Position, Position)], forceRewrite: Boolean): this.type = {
139+
if (!forceRewrite && this.children == children && pos.isEmpty)
140+
this
141+
else {
142+
assert(children.length == 2, s"AdtType : expected length 2 but got ${children.length}")
143+
val first = children.head.asInstanceOf[String]
144+
val second = children.tail.head.asInstanceOf[Map[TypeVar, Type]]
145+
AdtType(first, second)(this.typeParameters).asInstanceOf[this.type]
146+
}
147+
}
148+
149+
}
150+
151+
object AdtType {
152+
def apply(adt: Adt, typVarsMap: Map[TypeVar, Type]): AdtType =
153+
AdtType(adt.name, typVarsMap)(adt.typVars)
154+
}
155+
156+
157+
/**
158+
* This class represents a user-defined adt constructor application. See also the companion object below, which allows passing a
159+
* Adt constructor - this should be used in general for creation (so that typ is guaranteed to
160+
* be set correctly)
161+
*
162+
* @param name The name of the ADT constructor
163+
* @param args A sequence of expressions passed as arguments to the constructor
164+
* @param typVarMap Maps type parameters to (possibly concrete) types. May not map all
165+
* type parameters, may even be empty.
166+
* @param typ The return type of the constructor
167+
* @param adtName The corresponding ADT name
168+
*/
169+
case class AdtConstructorApp(name: String, args: Seq[Exp], typVarMap: Map[TypeVar, Type])
170+
(val pos: Position, val info: Info, override val typ: Type, val adtName: String, val errT: ErrorTrafo)
171+
extends ExtensionExp {
172+
override lazy val check: Seq[ConsistencyError] = args.flatMap(Consistency.checkPure)
173+
174+
override def prettyPrint: PrettyPrintPrimitives#Cont = {
175+
if (typVarMap.nonEmpty)
176+
// Type may be under-constrained, so to be safe we explicitly print out the type.
177+
parens(text(name) <> parens(ssep(args map show, char(',') <> space)) <> char(':') <+> show(typ))
178+
else
179+
text(name) <> parens(ssep(args map show, char(',') <> space))
180+
}
181+
182+
override def extensionIsPure: Boolean = true
183+
184+
override def extensionSubnodes: Seq[Node] = args ++ typVarMap.keys ++ typVarMap.values
185+
186+
override def verifyExtExp(): VerificationResult = {
187+
assert(assertion = false, "AdtConstructorApp: verifyExtExp has not been implemented.")
188+
Failure(Seq(ConsistencyError("AdtConstructorApp: verifyExtExp has not been implemented.", pos)))
189+
}
190+
191+
override def withChildren(children: Seq[Any], pos: Option[(Position, Position)], forceRewrite: Boolean): AdtConstructorApp.this.type = {
192+
if (!forceRewrite && this.children == children && pos.isEmpty)
193+
this
194+
else {
195+
assert(children.length == 3, s"AdtConstructorApp : expected length 3 but got ${children.length}")
196+
val first = children.head.asInstanceOf[String]
197+
val second = children(1).asInstanceOf[Seq[Exp]]
198+
val third = children(2).asInstanceOf[Map[TypeVar, Type]]
199+
AdtConstructorApp(first, second, third)(this.pos, this.info, this.typ, this.adtName, this.errT).asInstanceOf[this.type]
200+
}
201+
}
202+
}
203+
204+
object AdtConstructorApp {
205+
def apply(constructor: AdtConstructor, args: Seq[Exp], typVarMap: Map[TypeVar, Type])
206+
(pos: Position = NoPosition, info: Info = NoInfo, errT: ErrorTrafo = NoTrafos): AdtConstructorApp =
207+
AdtConstructorApp(constructor.name, args, typVarMap)(pos, info, constructor.typ.substitute(typVarMap), constructor.adtName, errT)
208+
}
209+
210+
/**
211+
* This class represents an adt destructor application. See also the companion object below, which allows passing a
212+
* Adt - this should be used in general for creation (so that typ is guaranteed to
213+
* be set correctly)
214+
*
215+
* @param name The name of the argument of an ADT constructor the destructor corresponds to
216+
* @param rcv An expression on with the the destructor is applied
217+
* @param typVarMap Maps type parameters to (possibly concrete) types. May not map all
218+
* type parameters, may even be empty.
219+
* @param typ The return type of the destructor
220+
* @param adtName The corresponding ADT name
221+
*/
222+
case class AdtDestructorApp(name: String, rcv: Exp, typVarMap: Map[TypeVar, Type])
223+
(val pos: Position, val info: Info, override val typ: Type, val adtName: String, val errT: ErrorTrafo) extends ExtensionExp {
224+
225+
override lazy val check: Seq[ConsistencyError] = Consistency.checkPure(rcv)
226+
227+
override def prettyPrint: PrettyPrintPrimitives#Cont = show(rcv) <> "." <> name
228+
229+
override def extensionIsPure: Boolean = true
230+
231+
override def extensionSubnodes: Seq[Node] = Seq(rcv) ++ typVarMap.keys ++ typVarMap.values
232+
233+
override def verifyExtExp(): VerificationResult = {
234+
assert(assertion = false, "AdtDestructorApp: verifyExtExp has not been implemented.")
235+
Failure(Seq(ConsistencyError("AdtDestructorApp: verifyExtExp has not been implemented.", pos)))
236+
}
237+
238+
override def withChildren(children: Seq[Any], pos: Option[(Position, Position)], forceRewrite: Boolean): this.type = {
239+
if (!forceRewrite && this.children == children && pos.isEmpty)
240+
this
241+
else {
242+
assert(children.length == 3, s"AdtDestructorApp : expected length 3 but got ${children.length}")
243+
val first = children.head.asInstanceOf[String]
244+
val second = children(1).asInstanceOf[Exp]
245+
val third = children(2).asInstanceOf[Map[TypeVar, Type]]
246+
AdtDestructorApp(first, second, third)(this.pos, this.info, this.typ, this.adtName, this.errT).asInstanceOf[this.type]
247+
}
248+
}
249+
}
250+
251+
object AdtDestructorApp {
252+
def apply(adt: Adt, name: String, rcv: Exp, typVarMap: Map[TypeVar, Type])
253+
(pos: Position = NoPosition, info: Info = NoInfo, errT: ErrorTrafo = NoTrafos): AdtDestructorApp = {
254+
val matchingConstructors = adt.constructors flatMap (c => c.formalArgs.filter { lv => lv.name == name })
255+
assert(matchingConstructors.length == 1, s"AdtDestructorApp : expected length 1 but got ${matchingConstructors.length}")
256+
val typ = matchingConstructors.head.typ
257+
AdtDestructorApp(name, rcv, typVarMap)(pos, info, typ.substitute(typVarMap), adt.name, errT)
258+
}
259+
}
260+
261+
/**
262+
* This class represents an adt discriminator application. See also the companion object below, which allows passing a
263+
* Adt - this should be used in general for creation (so that adtName is guaranteed to
264+
* be set correctly)
265+
*
266+
* @param name The name of the argument of an ADT constructor the destructor corresponds to
267+
* @param rcv An expression on with the the destructor is applied
268+
* @param typVarMap Maps type parameters to (possibly concrete) types. May not map all
269+
* type parameters, may even be empty.
270+
* @param adtName The corresponding ADT name
271+
*/
272+
case class AdtDiscriminatorApp(name: String, rcv: Exp, typVarMap: Map[TypeVar, Type])
273+
(val pos: Position, val info: Info, val adtName: String, val errT: ErrorTrafo) extends ExtensionExp {
274+
275+
override lazy val check: Seq[ConsistencyError] = Consistency.checkPure(rcv)
276+
277+
override def typ: Type = Bool
278+
279+
override def prettyPrint: PrettyPrintPrimitives#Cont = show(rcv) <> "." <> name <> "?"
280+
281+
override def extensionIsPure: Boolean = true
282+
283+
override def extensionSubnodes: Seq[Node] = Seq(rcv) ++ typVarMap.keys ++ typVarMap.values
284+
285+
override def verifyExtExp(): VerificationResult = {
286+
assert(assertion = false, "AdtDiscriminatorApp: verifyExtExp has not been implemented.")
287+
Failure(Seq(ConsistencyError("AdtDiscriminatorApp: verifyExtExp has not been implemented.", pos)))
288+
}
289+
290+
override def withChildren(children: Seq[Any], pos: Option[(Position, Position)], forceRewrite: Boolean): this.type = {
291+
if (!forceRewrite && this.children == children && pos.isEmpty)
292+
this
293+
else {
294+
assert(children.length == 3, s"AdtDestructorApp : expected length 3 but got ${children.length}")
295+
val first = children.head.asInstanceOf[String]
296+
val second = children(1).asInstanceOf[Exp]
297+
val third = children(2).asInstanceOf[Map[TypeVar, Type]]
298+
AdtDiscriminatorApp(first, second, third)(this.pos, this.info, this.adtName, this.errT).asInstanceOf[this.type]
299+
}
300+
}
301+
302+
}
303+
304+
object AdtDiscriminatorApp {
305+
def apply(adt: Adt, name: String, rcv: Exp, typVarMap: Map[TypeVar, Type])
306+
(pos: Position = NoPosition, info: Info = NoInfo, errT: ErrorTrafo = NoTrafos): AdtDiscriminatorApp = {
307+
AdtDiscriminatorApp(name, rcv, typVarMap)(pos, info, adt.name, errT)
308+
}
309+
}
310+

0 commit comments

Comments
 (0)