Skip to content
Merged
Show file tree
Hide file tree
Changes from 55 commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
8cf2805
Implement PAST nodes for ADT Declaration
amaissen Feb 25, 2022
a726400
Add AST nodes for ADT Declaration
amaissen Feb 25, 2022
8084def
Implement translation of ADT declaration and ADT constructors to AST …
amaissen Feb 25, 2022
a3d5c7a
Extend PAST with a new type for ADT's
amaissen Feb 25, 2022
e0d78f6
Properly implement the method withChildren for PAdtConstructor
amaissen Feb 25, 2022
c358505
Translate PDomainType to PAdtType if necessary
amaissen Feb 25, 2022
48326cd
Implement type-checking for ADT declaration, ADT constructors and ADT…
amaissen Feb 25, 2022
9fc8bee
Add an AST node for the ADT type
amaissen Feb 25, 2022
20b0ab9
Enhance PrettyPrinter
amaissen Feb 24, 2022
cbcd1d0
Change order of member signature translation
amaissen Feb 24, 2022
01e67e2
Add prettyPrinting for extension AST nodes
amaissen Feb 25, 2022
e9737c4
Finalize translation of PAST nodes to AST nodes for ADT declarations
amaissen Feb 25, 2022
9e193c2
Resolve TODO
amaissen Feb 25, 2022
7e4bb7a
Implement method withChildren for AdtConstructor and AdtType
amaissen Mar 2, 2022
1952c7f
Fix issue in rewriting strategy
amaissen Mar 2, 2022
ecaf1c6
Resolve TODO
amaissen Mar 2, 2022
72cb46a
Created a new class for the AdtEncoder
amaissen Mar 2, 2022
22a56b9
Use the AdtEncoder
amaissen Mar 2, 2022
c960afc
Add a PAST node for an ADT constructor call
amaissen Mar 3, 2022
ff73fe5
Add return type to AdtConstructor
amaissen Mar 4, 2022
5faa7c0
Add an AST node for an ADT constructor application
amaissen Mar 4, 2022
9476942
Fix type checking for constructor call and add implement transaltion
amaissen Mar 4, 2022
2245302
Add an AdtNameManager
amaissen Mar 11, 2022
a5b0856
Finalize main encoding of ADTs as domain functions
amaissen Mar 11, 2022
2432860
The type variable mapping should be set if there are type variables
amaissen Mar 11, 2022
ac8429c
Extend ADT type checker
amaissen Mar 11, 2022
d3146c3
Enable ADT constructor calls
amaissen Mar 16, 2022
e118d57
Merge branch 'amaissen/adjust-plugin-infrastructure' into add-adt-pas…
amaissen Mar 16, 2022
051999e
Merge branch 'amaissen/fix-plugin-infrastructure-in-resolver' into ad…
amaissen Mar 16, 2022
a0b4f57
Merge branch 'amaissen/fix-strategy-builder' into add-adt-past-extension
amaissen Mar 16, 2022
7c1f171
Adjust to updated plugin infrastructure
amaissen Mar 16, 2022
9b85472
Prepare for adding destructors
amaissen Mar 16, 2022
035c321
Add an AST node for a destructor application
amaissen Mar 16, 2022
5ea759c
Add encoding for ADT destructor calls
amaissen Mar 16, 2022
09b3cdd
Add a Parser AST node for destructor calls and implement typechecking
amaissen Mar 16, 2022
eb28ae3
Enable destructor calls
amaissen Mar 16, 2022
ee6ca75
Add an AST node for an adt discriminator application
amaissen Mar 17, 2022
6e42ce7
Add encoding for adt discriminator applications
amaissen Mar 17, 2022
be46785
Add parser AST node for adt discriminator call
amaissen Mar 17, 2022
df6af71
Merge branch 'master' into add-adt-extension
amaissen Mar 21, 2022
3cf21ed
Fix issue in resolving constructor calls with wrong number of arguments
amaissen Mar 21, 2022
8a2530e
Fix issue in tag axiom
amaissen Mar 22, 2022
9ba76b8
Fix issue in AdtNameManager
amaissen Mar 22, 2022
71b1c4d
Fix issue in resolving discriminator calls
amaissen Mar 22, 2022
a761dcb
Fix the discriminator syntax and its parsing
amaissen Mar 29, 2022
4fd759e
Add test resources for adt plugin
amaissen Mar 29, 2022
cacd64b
Add contains function for ADTs
amaissen Mar 31, 2022
558cbb0
Implement Syntax for Deriving System
amaissen Apr 11, 2022
e83d75b
Merge branch 'viperproject:master' into add-adt-extension
amaissen Apr 11, 2022
993e2a3
Fix small issue in syntax for deriving functions
amaissen Apr 12, 2022
5120fa9
Add test for deriving system and derivable contains function
amaissen Apr 12, 2022
8de12f3
Minor changes in ADT constructor argument handling
amaissen Apr 12, 2022
f55e455
Make the ADT plugin a default plugin
amaissen Apr 25, 2022
1ce41ce
Merge pull request #1 from amaissen/add-adt-extension
amaissen Apr 25, 2022
4e4d861
Fix the plugin tests
amaissen Apr 26, 2022
81b2a02
Fix some whitespace and formatting issues
amaissen Apr 26, 2022
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
9 changes: 9 additions & 0 deletions src/main/resources/import/adt/contains.vpr
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// Copyright (c) 2011-2022 ETH Zurich.

domain ContainsDomain[A,B] {
function contains(a: A, b: B): Bool
}
1 change: 1 addition & 0 deletions src/main/resources/import/adt/derives.vpr
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import <adt/contains.vpr>
7 changes: 7 additions & 0 deletions src/main/scala/viper/silver/frontend/SilFrontEndConfig.scala
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,13 @@ abstract class SilFrontendConfig(args: Seq[String], private var projectName: Str
hidden = true
)

val adtPlugin = opt[Boolean]("disableAdtPlugin",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know this is the same as the termination plugin above, but shouldn't this rather be enableAdtPlugin, true by default?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Aurel300 I don't have a strong opinion here. If you have, please bring it up in a Viper meeting. Or simply change the implementation when nobody is looking ;-)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Imo, the bigger issue with disabling default plugins is that they are actually still loaded. Disabling of the plugin's functionality happens by returning from the hooks without doing anything. I adopted this from the termination plugin, personally I don't like this solution. :)

descr = "Disable the ADT plugin, which adds support for ADTs as a built-in type.",
default = Some(false),
noshort = true,
hidden = true
)

val assumeInjectivityOnInhale = opt[Boolean]("assumeInjectivityOnInhale",
descr = "Assumes injectivity of the receiver expression when inhaling quantified permissions, instead of checking it.",
default = Some(false),
Expand Down
1 change: 1 addition & 0 deletions src/main/scala/viper/silver/frontend/SilFrontend.scala
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ trait SilFrontend extends DefaultFrontend {
* All default plugins can be excluded from the plugins by providing the --disableDefaultPlugins flag
*/
private val defaultPlugins: Seq[String] = Seq(
"viper.silver.plugin.standard.adt.AdtPlugin",
"viper.silver.plugin.standard.termination.TerminationPlugin",
"viper.silver.plugin.standard.predicateinstance.PredicateInstancePlugin"
)
Expand Down
302 changes: 302 additions & 0 deletions src/main/scala/viper/silver/plugin/standard/adt/AdtASTExtension.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,302 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// Copyright (c) 2011-2022 ETH Zurich.

package viper.silver.plugin.standard.adt

import viper.silver.ast._
import viper.silver.ast.pretty.FastPrettyPrinter.{ContOps, braces, brackets, char, defaultIndent, line, nest, nil, parens, show, showType, showVars, space, ssep, text}
import viper.silver.ast.pretty.PrettyPrintPrimitives
import viper.silver.ast.utility.Consistency
import viper.silver.verifier.{ConsistencyError, Failure, VerificationResult}

/**
* This class represents a user-defined ADT.
*
* @param name name of the ADT
* @param constructors sequence of corresponding constructors
* @param typVars sequence of type variables (generics)
* @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
*/
case class Adt(name: String, constructors: Seq[AdtConstructor], typVars: Seq[TypeVar] = Nil, derivingInfo: Map[String, (Option[Type], Set[String])] = Map.empty)
(val pos: Position = NoPosition, val info: Info = NoInfo, val errT: ErrorTrafo = NoTrafos) extends ExtensionMember {
val scopedDecls: Seq[Declaration] = Seq()

override def extensionSubnodes: Seq[Node] = constructors ++ typVars ++ derivingInfo.map(_._2._1).collect { case Some(v) => v }

override def prettyPrint: PrettyPrintPrimitives#Cont = {

def showDerivingInfo(di: (String, (Option[Type], Set[String]))): PrettyPrintPrimitives#Cont = {
val (func, (param, blocklist)) = di
text(func) <> (if (param.isEmpty) nil else text("[") <> showType(param.get) <> space <> "]") <>
space <> (if (blocklist.isEmpty) nil else text("without") <> space <> ssep(blocklist.toSeq map text, char (',') <> space))
}

text("adt") <+> name <>
(if (typVars.isEmpty) nil else text("[") <> ssep(typVars map show, char (',') <> space) <> "]") <+>
braces(nest(defaultIndent,
line <> line <>
ssep(constructors map show, line <> line)
) <> line) <+>
(if (derivingInfo.isEmpty) nil else text("derives") <+>
braces(nest(defaultIndent,
line <> line <>
ssep(derivingInfo.toSeq map showDerivingInfo, line <> line)
) <> line)
)
}
}

/**
* This class represents an ADT constructor. See also the companion object below, which allows passing a
* Adt - this should be used in general for creation (so that typ is guaranteed to
* be set correctly)
*
* @param name name of the ADT constructor
* @param formalArgs sequence of arguments of the constructor
* @param typ the return type of the constructor
* @param adtName the name corresponding of the corresponding ADT
*/
case class AdtConstructor(name: String, formalArgs: Seq[LocalVarDecl])
(val pos: Position, val info: Info, val typ: AdtType, val adtName : String, val errT: ErrorTrafo)
extends ExtensionMember {

override def getMetadata:Seq[Any] = {
Seq(pos, info, errT)
}
val scopedDecls: Seq[Declaration] = formalArgs

override def extensionSubnodes: Seq[Node] = formalArgs

override def prettyPrint: PrettyPrintPrimitives#Cont = text(name) <> parens(showVars(formalArgs))

override def withChildren(children: Seq[Any], pos: Option[(Position, Position)], forceRewrite: Boolean): this.type = {
if (!forceRewrite && this.children == children && pos.isEmpty)
this
else {
assert(children.length == 2, s"AdtConstructor : expected length 2 but got ${children.length}")
val first = children.head.asInstanceOf[String]
val second = children.tail.head.asInstanceOf[Seq[LocalVarDecl]]
AdtConstructor(first, second)(this.pos, this.info, this.typ, this.adtName, this.errT).asInstanceOf[this.type]
}

}
}

object AdtConstructor {
def apply(adt: Adt, name: String, formalArgs: Seq[LocalVarDecl])(pos: Position = NoPosition, info: Info = NoInfo, errT: ErrorTrafo = NoTrafos): AdtConstructor = {
AdtConstructor(name, formalArgs)(pos, info, AdtType(adt, (adt.typVars zip adt.typVars).toMap), adt.name, errT)
}
}

/**
* This class represents an user-defined ADT type. See also the companion object below, which allows passing a
* Adt - this should be used in general for creation (so that typeParameters is guaranteed to
* be set correctly)
*
* @param adtName The name of the underlying adt.
* @param partialTypVarsMap Maps type parameters to (possibly concrete) types. May not map all
* type parameters, may even be empty.
* @param typeParameters The type variables of the ADT type.
*/
case class AdtType(adtName: String, partialTypVarsMap: Map[TypeVar, Type])
(val typeParameters: Seq[TypeVar]) extends ExtensionType {

override lazy val check: Seq[ConsistencyError] = if(!(typeParameters.toSet == typVarsMap.keys.toSet)) {
Seq(ConsistencyError(s"${typeParameters.toSet} doesn't equal ${typVarsMap.keys.toSet}", NoPosition))
} else Seq()

/**
* Map each type parameter `A` to `partialTypVarsMap(A)`, if defined, otherwise to `A` itself.
* `typVarsMap` thus contains a mapping for each type parameter.
*/
val typVarsMap: Map[TypeVar, Type] =
typeParameters.map(tp => tp -> partialTypVarsMap.getOrElse(tp, tp)).to(implicitly)

/**
* Takes a mapping of type variables to types and substitutes all
* occurrences of those type variables with the corresponding type.
*/
override def substitute(newTypVarsMap: Map[TypeVar, Type]): Type = {
assert (typVarsMap.keys.toSet equals this.typeParameters.toSet)
val newTypeMap = typVarsMap.map(kv=>kv._1 -> kv._2.substitute(newTypVarsMap))
AdtType(adtName, newTypeMap)(typeParameters)
}

/** Is this a concrete type (i.e. no uninstantiated type variables)? */
override def isConcrete: Boolean = typVarsMap.values.forall(_.isConcrete)

override def prettyPrint: PrettyPrintPrimitives#Cont = {
val typArgs = typeParameters map (t => show(typVarsMap.getOrElse(t, t)))
text(adtName) <> (if (typArgs.isEmpty) nil else brackets(ssep(typArgs, char (',') <> space)))
}

override def withChildren(children: Seq[Any], pos: Option[(Position, Position)], forceRewrite: Boolean): this.type = {
if (!forceRewrite && this.children == children && pos.isEmpty)
this
else {
assert(children.length == 2, s"AdtType : expected length 2 but got ${children.length}")
val first = children.head.asInstanceOf[String]
val second = children.tail.head.asInstanceOf[Map[TypeVar, Type]]
AdtType(first, second)(this.typeParameters).asInstanceOf[this.type]
}
}

}

object AdtType{
def apply(adt: Adt, typVarsMap: Map[TypeVar, Type]): AdtType =
AdtType(adt.name, typVarsMap)(adt.typVars)
}


/**
* This class represents a user-defined adt constructor application. See also the companion object below, which allows passing a
* Adt constructor - this should be used in general for creation (so that typ is guaranteed to
* be set correctly)
*
* @param name The name of the ADT constructor
* @param args A sequence of expressions passed as arguments to the constructor
* @param typVarMap Maps type parameters to (possibly concrete) types. May not map all
* type parameters, may even be empty.
* @param typ The return type of the constructor
* @param adtName The corresponding ADT name
*/
case class AdtConstructorApp(name: String, args: Seq[Exp], typVarMap: Map[TypeVar, Type])
(val pos: Position, val info: Info, override val typ: Type, val adtName:String, val errT: ErrorTrafo)
extends ExtensionExp {
override lazy val check : Seq[ConsistencyError] = args.flatMap(Consistency.checkPure)

override def prettyPrint: PrettyPrintPrimitives#Cont = {
if (typVarMap.nonEmpty)
// Type may be under-constrained, so to be safe we explicitly print out the type.
parens(text(name) <> parens(ssep(args map show, char (',') <> space)) <> char(':') <+> show(typ))
else
text(name) <> parens(ssep(args map show, char (',') <> space))
}

override def extensionIsPure: Boolean = true

override def extensionSubnodes: Seq[Node] = args ++ typVarMap.keys ++ typVarMap.values

override def verifyExtExp(): VerificationResult = {
assert(assertion = false, "AdtConstructorApp: verifyExtExp has not been implemented.")
Failure(Seq(ConsistencyError("AdtConstructorApp: verifyExtExp has not been implemented.", pos)))
}

override def withChildren(children: Seq[Any], pos: Option[(Position, Position)], forceRewrite: Boolean): AdtConstructorApp.this.type = {
if (!forceRewrite && this.children == children && pos.isEmpty)
this
else {
assert(children.length == 3, s"AdtConstructorApp : expected length 3 but got ${children.length}")
val first = children.head.asInstanceOf[String]
val second = children(1).asInstanceOf[Seq[Exp]]
val third = children(2).asInstanceOf[Map[TypeVar, Type]]
AdtConstructorApp(first, second, third)(this.pos, this.info, this.typ, this.adtName, this.errT).asInstanceOf[this.type]
}
}
}
object AdtConstructorApp {
def apply(constructor : AdtConstructor, args: Seq[Exp], typVarMap: Map[TypeVar, Type])(pos: Position = NoPosition, info: Info = NoInfo, errT: ErrorTrafo = NoTrafos) : AdtConstructorApp =
AdtConstructorApp(constructor.name, args, typVarMap)(pos, info, constructor.typ.substitute(typVarMap), constructor.adtName, errT)
}

/**
* This class represents an adt destructor application. See also the companion object below, which allows passing a
* Adt - this should be used in general for creation (so that typ is guaranteed to
* be set correctly)
*
* @param name The name of the argument of an ADT constructor the destructor corresponds to
* @param rcv An expression on with the the destructor is applied
* @param typVarMap Maps type parameters to (possibly concrete) types. May not map all
* type parameters, may even be empty.
* @param typ The return type of the destructor
* @param adtName The corresponding ADT name
*/
case class AdtDestructorApp(name: String, rcv: Exp, typVarMap: Map[TypeVar, Type])(val pos: Position, val info: Info, override val typ: Type, val adtName:String, val errT: ErrorTrafo) extends ExtensionExp {

override lazy val check : Seq[ConsistencyError] = Consistency.checkPure(rcv)

override def prettyPrint: PrettyPrintPrimitives#Cont = show(rcv) <> "." <> name

override def extensionIsPure: Boolean = true

override def extensionSubnodes: Seq[Node] = Seq(rcv) ++ typVarMap.keys ++ typVarMap.values

override def verifyExtExp(): VerificationResult = {
assert(assertion = false, "AdtDestructorApp: verifyExtExp has not been implemented.")
Failure(Seq(ConsistencyError("AdtDestructorApp: verifyExtExp has not been implemented.", pos)))
}

override def withChildren(children: Seq[Any], pos: Option[(Position, Position)], forceRewrite: Boolean): this.type = {
if (!forceRewrite && this.children == children && pos.isEmpty)
this
else {
assert(children.length == 3, s"AdtDestructorApp : expected length 3 but got ${children.length}")
val first = children.head.asInstanceOf[String]
val second = children(1).asInstanceOf[Exp]
val third = children(2).asInstanceOf[Map[TypeVar, Type]]
AdtDestructorApp(first, second, third)(this.pos, this.info, this.typ, this.adtName, this.errT).asInstanceOf[this.type]
}
}
}

object AdtDestructorApp {
def apply(adt : Adt, name: String, rcv: Exp, typVarMap: Map[TypeVar, Type])(pos: Position = NoPosition, info: Info = NoInfo, errT: ErrorTrafo = NoTrafos) : AdtDestructorApp = {
val matchingConstructors = adt.constructors flatMap (c => c.formalArgs.filter { lv => lv.name == name })
assert(matchingConstructors.length == 1, s"AdtDestructorApp : expected length 1 but got ${matchingConstructors.length}")
val typ = matchingConstructors.head.typ
AdtDestructorApp(name, rcv, typVarMap)(pos, info, typ.substitute(typVarMap), adt.name, errT)
}
}

/**
* This class represents an adt discriminator application. See also the companion object below, which allows passing a
* Adt - this should be used in general for creation (so that adtName is guaranteed to
* be set correctly)
*
* @param name The name of the argument of an ADT constructor the destructor corresponds to
* @param rcv An expression on with the the destructor is applied
* @param typVarMap Maps type parameters to (possibly concrete) types. May not map all
* type parameters, may even be empty.
* @param adtName The corresponding ADT name
*/
case class AdtDiscriminatorApp(name: String, rcv: Exp, typVarMap: Map[TypeVar, Type])(val pos: Position, val info: Info, val adtName:String, val errT: ErrorTrafo) extends ExtensionExp {

override def typ: Type = Bool

override lazy val check : Seq[ConsistencyError] = Consistency.checkPure(rcv)

override def prettyPrint: PrettyPrintPrimitives#Cont = show(rcv) <> "." <> name <> "?"

override def extensionIsPure: Boolean = true

override def extensionSubnodes: Seq[Node] = Seq(rcv) ++ typVarMap.keys ++ typVarMap.values

override def verifyExtExp(): VerificationResult = {
assert(assertion = false, "AdtDiscriminatorApp: verifyExtExp has not been implemented.")
Failure(Seq(ConsistencyError("AdtDiscriminatorApp: verifyExtExp has not been implemented.", pos)))
}
Comment on lines +285 to +288

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this an open TODO?

@amaissen amaissen Apr 26, 2022

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, verifyExtExp() is a method provided by the plugin infrastructure, but actually never used anywhere. If, this method will be used/called in future, the error should happen, since it should not be necessary to call it for new expression added with the ADT plugin.


override def withChildren(children: Seq[Any], pos: Option[(Position, Position)], forceRewrite: Boolean): this.type = {
if (!forceRewrite && this.children == children && pos.isEmpty)
this
else {
assert(children.length == 3, s"AdtDestructorApp : expected length 3 but got ${children.length}")
val first = children.head.asInstanceOf[String]
val second = children(1).asInstanceOf[Exp]
val third = children(2).asInstanceOf[Map[TypeVar, Type]]
AdtDiscriminatorApp(first, second, third)(this.pos, this.info, this.adtName, this.errT).asInstanceOf[this.type]
}
}

}

object AdtDiscriminatorApp {
def apply(adt : Adt, name: String, rcv: Exp, typVarMap: Map[TypeVar, Type])(pos: Position = NoPosition, info: Info = NoInfo, errT: ErrorTrafo = NoTrafos) : AdtDiscriminatorApp = {
AdtDiscriminatorApp(name, rcv, typVarMap)(pos, info, adt.name, errT)
}
}

Loading