Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix issue 491 #531

Open
wants to merge 16 commits into
base: master
Choose a base branch
from
3 changes: 2 additions & 1 deletion src/main/scala/viper/gobra/ast/internal/PrettyPrinter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -569,7 +569,7 @@ class DefaultPrettyPrinter extends PrettyPrinter with kiama.output.PrettyPrinter
case PermTExpr() => "perm"
case PointerTExpr(elem) => "*" <> showExpr(elem)
case StructTExpr(fs) => "struct" <> braces(showList(fs)(f => f._1 <> ":" <+> showExpr(f._2)))
case ArrayTExpr(len, elem) => brackets(showExpr(len)) <> showExpr(elem)
case ArrayTExpr(len, elem) => brackets(len.toString()) <> showExpr(elem)
case SliceTExpr(elem) => brackets(emptyDoc) <> showExpr(elem)
case MapTExpr(key, elem) => "map" <> brackets(showExpr(key) <> comma <+> showExpr(elem))
case SequenceTExpr(elem) => "seq" <> brackets(showExpr(elem))
Expand All @@ -583,6 +583,7 @@ class DefaultPrettyPrinter extends PrettyPrinter with kiama.output.PrettyPrinter
case DfltVal(typ) => "dflt" <> brackets(showType(typ))
case Tuple(args) => parens(showExprList(args))
case Deref(exp, _) => "*" <> showExpr(exp)
case UncheckedRef(ref, _) => "&" <> showAddressable(ref)
case Ref(ref, _) => "&" <> showAddressable(ref)
case FieldRef(recv, field) => showExpr(recv) <> "." <> field.name
case StructUpdate(base, field, newVal) => showExpr(base) <> brackets(showField(field) <+> ":=" <+> showExpr(newVal))
Expand Down
15 changes: 13 additions & 2 deletions src/main/scala/viper/gobra/ast/internal/Program.scala
Original file line number Diff line number Diff line change
Expand Up @@ -645,7 +645,7 @@ case class Float32TExpr()(val info: Source.Parser.Info) extends TypeExpr
case class Float64TExpr()(val info: Source.Parser.Info) extends TypeExpr
case class IntTExpr(kind: IntegerKind)(val info: Source.Parser.Info) extends TypeExpr
case class StructTExpr(fields: Vector[(String, Expr, Boolean)])(val info: Source.Parser.Info) extends TypeExpr
case class ArrayTExpr(length: Expr, elems: Expr)(val info: Source.Parser.Info) extends TypeExpr
case class ArrayTExpr(length: BigInt, elems: Expr)(val info: Source.Parser.Info) extends TypeExpr
case class SliceTExpr(elems: Expr)(val info: Source.Parser.Info) extends TypeExpr
case class MapTExpr(keys: Expr, elems: Expr)(val info: Source.Parser.Info) extends TypeExpr
case class PermTExpr()(val info: Source.Parser.Info) extends TypeExpr
Expand Down Expand Up @@ -945,7 +945,18 @@ case class Deref(exp: Expr, underlyingTypeExpr: Type)(val info: Source.Parser.In
override val typ: Type = underlyingTypeExpr.asInstanceOf[PointerT].t
}

case class Ref(ref: Addressable, typ: PointerT)(val info: Source.Parser.Info) extends Expr with Location
/** Only used internally to separate the type encodings. Should not be created by the desugarer. */
case class UncheckedRef(ref: Addressable, typ: PointerT)(val info: Source.Parser.Info) extends Expr {
def checked: Ref = Ref(ref, typ)(info)
}

object UncheckedRef {
def apply(exp: Expr)(info: Source.Parser.Info): UncheckedRef = Ref(exp)(info).unchecked
}

case class Ref(ref: Addressable, typ: PointerT)(val info: Source.Parser.Info) extends Expr {
def unchecked: UncheckedRef = UncheckedRef(ref, typ)(info)
}

object Ref {
def apply(ref: Expr)(info: Source.Parser.Info): Ref = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ object Comparability {

case _: TypeHead.StructHD => Kind.Recursive
case _: TypeHead.FunctionHD => Kind.NonComparable
case TypeHead.ArrayHD => Kind.Recursive
case _: TypeHead.ArrayHD => Kind.Recursive
case TypeHead.SliceHD => Kind.NonComparable
case TypeHead.MapHD => Kind.NonComparable
case _: TypeHead.InterfaceHD => Kind.Dynamic
Expand Down
18 changes: 13 additions & 5 deletions src/main/scala/viper/gobra/ast/internal/theory/TypeHead.scala
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ object TypeHead {
case class FunctionHD(arity: Int) extends TypeHead
/** 'fields' stores for each field the name and whether the field is ghost. */
case class StructHD(fields: Vector[(String, Boolean)]) extends TypeHead
case object ArrayHD extends TypeHead
case class ArrayHD(size: BigInt) extends TypeHead
case object SliceHD extends TypeHead
case object MapHD extends TypeHead
case class InterfaceHD(name: String) extends TypeHead
Expand Down Expand Up @@ -74,7 +74,7 @@ object TypeHead {
case VoidT => UnitHD
case _: PermissionT => PermHD
case SortT => SortHD
case _: ArrayT => ArrayHD
case t: ArrayT => ArrayHD(t.length)
case _: SliceT => SliceHD
case _: MapT => MapHD
case _: SequenceT => SeqHD
Expand Down Expand Up @@ -151,7 +151,7 @@ object TypeHead {
case _: PointerTExpr => PointerHD
case t: DefinedTExpr => DefinedHD(t.name)
case t: StructTExpr => StructHD(t.fields.map(t => (t._1, t._3)))
case _: ArrayTExpr => ArrayHD
case t: ArrayTExpr => ArrayHD(t.length)
case _: SliceTExpr => SliceHD
case _: MapTExpr => MapHD
case _: PermTExpr => PermHD
Expand All @@ -174,7 +174,7 @@ object TypeHead {
case _: DefinedHD => 0
case t: FunctionHD => t.arity
case t: StructHD => t.fields.size
case ArrayHD => 1
case _: ArrayHD => 1
case SliceHD => 1
case MapHD => 2
case _: InterfaceHD => 0
Expand All @@ -195,5 +195,13 @@ object TypeHead {
case t: PredHD => t.arity
}


def isZeroSize(t: Type)(ctx: LookupTable): Boolean = isZeroSize(typeTree(t))(ctx)
def isZeroSize(t: RoseTree[TypeHead])(ctx: LookupTable): Boolean = {
t match {
case RoseTree(_: StructHD, children) => children.forall(isZeroSize(_)(ctx))
case RoseTree(hd: ArrayHD, children) => hd.size == 0 || children.forall(isZeroSize(_)(ctx))
case RoseTree(hd: DefinedHD, _) => isZeroSize(ctx.lookup(DefinedT(hd.name, Addressability.Exclusive)))(ctx)
case _ => false
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ object CGEdgesTerminationTransform extends InternalTransform {
case in.IntT(_, kind) => in.IntTExpr(kind)(src)
case in.StringT(_) => in.StringTExpr()(src)
case in.PermissionT(_) => in.PermTExpr()(src)
case in.ArrayT(length, elems, _) => in.ArrayTExpr(in.IntLit(length)(src), typeAsExpr(elems)(src))(src)
case in.ArrayT(length, elems, _) => in.ArrayTExpr(length, typeAsExpr(elems)(src))(src)
case in.SliceT(elems, _) => in.SliceTExpr(typeAsExpr(elems)(src))(src)
case in.MapT(keys, values, _) => in.MapTExpr(typeAsExpr(keys)(src), typeAsExpr(values)(src))(src)
case in.SequenceT(t, _) => in.SequenceTExpr(typeAsExpr(t)(src))(src)
Expand Down
3 changes: 1 addition & 2 deletions src/main/scala/viper/gobra/frontend/Desugar.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2965,9 +2965,8 @@ object Desugar extends LazyLogging {

case PArrayType(len, elem) =>
for {
inLen <- exprD(ctx, info)(len)
inElem <- go(elem)
} yield in.ArrayTExpr(inLen, inElem)(src)
} yield in.ArrayTExpr(info.evalInt(len), inElem)(src)

case PSliceType(elem) =>
for {
Expand Down
5 changes: 5 additions & 0 deletions src/main/scala/viper/gobra/frontend/info/TypeInfo.scala
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,9 @@ trait TypeInfo extends ExternalTypeInfo {
def freeDeclared(n: PNode): Vector[PIdnNode]

def capturedLocalVariables(decl: PClosureDecl): Vector[PIdnNode]

def evalBool(exp: PExpression): Boolean
def evalInt(exp: PExpression): BigInt
def evalString(exp: PExpression): String
def evalPerm(exp: PExpression): (BigInt, BigInt)
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,23 @@ import viper.gobra.util.Violation.violation

trait ConstantEvaluation { this: TypeInfoImpl =>

def evalBool(exp: PExpression): Boolean = {
boolConstantEval(exp).getOrElse(violation(s"expected constant bool expression, but got $exp"))
}

def evalInt(exp: PExpression): BigInt = {
intConstantEval(exp).getOrElse(violation(s"expected constant int expression, but got $exp"))
}

def evalString(exp: PExpression): String = {
stringConstantEval(exp).getOrElse(violation(s"expected constant string expression, but got $exp"))
}

def evalPerm(exp: PExpression): (BigInt, BigInt) = {
permConstantEval(exp).getOrElse(violation(s"expected constant permission expression, but got $exp"))
}


lazy val boolConstantEval: PExpression => Option[Boolean] =
attr[PExpression, Option[Boolean]] {
case PBoolLit(lit) => Some(lit)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ object DefaultErrorBackTranslator {

val transformVerificationErrorReason: VerificationErrorReason => VerificationErrorReason = {
case AssertionFalseError(info / OverflowCheckAnnotation) => OverflowErrorReason(info)
case AssertionFalseError(info / ReceiverNotNilCheckAnnotation) => InterfaceReceiverIsNilReason(info)
case AssertionFalseError(info / ReceiverNotNilCheckAnnotation) => ReceiverIsNilReason(info)
case x => x
}

Expand Down
4 changes: 2 additions & 2 deletions src/main/scala/viper/gobra/reporting/VerifierError.scala
Original file line number Diff line number Diff line change
Expand Up @@ -448,9 +448,9 @@ case class OverflowErrorReason(node: Source.Verifier.Info) extends VerificationE
override def message: String = s"Expression ${node.origin.tag.trim} might cause integer overflow."
}

case class InterfaceReceiverIsNilReason(node: Source.Verifier.Info) extends VerificationErrorReason {
case class ReceiverIsNilReason(node: Source.Verifier.Info) extends VerificationErrorReason {
override def id: String = "receiver_is_nil_error"
override def message: String = s"The receiver might be nil"
override def message: String = s"The receiver ${node.origin.tag.trim} might be nil"
}

case class DynamicValueNotASubtypeReason(node: Source.Verifier.Info) extends VerificationErrorReason {
Expand Down
34 changes: 29 additions & 5 deletions src/main/scala/viper/gobra/translator/context/Context.scala
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ trait Context {

def reference(x: in.Location): CodeWriter[vpr.Exp] = typeEncoding.reference(this)(x)

def safeReference(x: in.Location): CodeWriter[vpr.Exp] = typeEncoding.safeReference(this)(x)

def value(x: in.Expr): CodeWriter[vpr.Exp] = typeEncoding.value(this)(x)

def footprint(x: in.Location, perm: in.Expr): CodeWriter[vpr.Exp] = typeEncoding.addressFootprint(this)(x, perm)
Expand Down Expand Up @@ -138,15 +140,14 @@ trait Context {
}

// mapping

def addVars(vars: vpr.LocalVarDecl*): Context

// fresh variable counter
/** publicly exposed infinite iterator providing fresh names */
def freshNames: Iterator[String] = internalFreshNames

/** internal fresh name iterator that additionally provides a getter function for its counter value */
protected def internalFreshNames: FreshNameIterator
protected def internalFreshNames: Context.FreshNameIterator

/** copy constructor */
def :=(
Expand All @@ -165,7 +166,28 @@ trait Context {
unknownValueN: UnknownValues = unknownValue,
typeEncodingN: TypeEncoding = typeEncoding,
defaultEncodingN: DefaultEncoding = defaultEncoding,
initialFreshCounterValueN: Int = internalFreshNames.getValue
initialFreshCounterValueN: Option[Int] = None,
): Context = {
update(fieldN, arrayN, seqToSetN, seqToMultisetN, seqMultiplicityN, optionN, optionToSeqN, sliceN, fixpointN, tupleN, equalityN, conditionN, unknownValueN, typeEncodingN, defaultEncodingN, initialFreshCounterValueN)
}

protected def update(
fieldN: Fields,
arrayN: Arrays,
seqToSetN: SeqToSet,
seqToMultisetN: SeqToMultiset,
seqMultiplicityN: SeqMultiplicity,
optionN: Options,
optionToSeqN: OptionToSeq,
sliceN: Slices,
fixpointN: Fixpoint,
tupleN: Tuples,
equalityN: Equality,
conditionN: Conditions,
unknownValueN: UnknownValues,
typeEncodingN: TypeEncoding,
defaultEncodingN: DefaultEncoding,
initialFreshCounterValueN: Option[Int],
): Context


Expand All @@ -189,8 +211,10 @@ trait Context {
col.finalize(typeEncoding)
}

}

object Context {
trait FreshNameIterator extends Iterator[String] {
def getValue: Int
}

}
}
86 changes: 45 additions & 41 deletions src/main/scala/viper/gobra/translator/context/ContextImpl.scala
Original file line number Diff line number Diff line change
Expand Up @@ -22,25 +22,25 @@ import viper.gobra.translator.library.tuples.Tuples
import viper.gobra.translator.library.unknowns.UnknownValues
import viper.silver.ast.LocalVarDecl

case class ContextImpl(
field: Fields,
array: Arrays,
seqToSet: SeqToSet,
seqToMultiset: SeqToMultiset,
seqMultiplicity: SeqMultiplicity,
option: Options,
optionToSeq: OptionToSeq,
slice: Slices,
fixpoint: Fixpoint,
tuple: Tuples,
equality: Equality,
condition: Conditions,
unknownValue: UnknownValues,
typeEncoding: TypeEncoding,
defaultEncoding: DefaultEncoding,
table: LookupTable,
initialFreshCounterValue: Int = 0
) extends Context {
class ContextImpl(
override val field: Fields,
override val array: Arrays,
override val seqToSet: SeqToSet,
override val seqToMultiset: SeqToMultiset,
override val seqMultiplicity: SeqMultiplicity,
override val option: Options,
override val optionToSeq: OptionToSeq,
override val slice: Slices,
override val fixpoint: Fixpoint,
override val tuple: Tuples,
override val equality: Equality,
override val condition: Conditions,
override val unknownValue: UnknownValues,
override val typeEncoding: TypeEncoding,
override val defaultEncoding: DefaultEncoding,
override val table: LookupTable,
override val internalFreshNames: Context.FreshNameIterator = ContextImpl.FreshNameIteratorImpl(0),
) extends Context {

def this(conf: TranslatorConfig, table: LookupTable) = {
this(
Expand All @@ -64,24 +64,24 @@ case class ContextImpl(
}

/** copy constructor */
override def :=(
fieldN: Fields = field,
arrayN: Arrays = array,
seqToSetN: SeqToSet = seqToSet,
seqToMultisetN: SeqToMultiset = seqToMultiset,
seqMultiplicityN: SeqMultiplicity = seqMultiplicity,
optionN: Options = option,
optionToSeqN: OptionToSeq = optionToSeq,
sliceN: Slices = slice,
fixpointN: Fixpoint = fixpoint,
tupleN: Tuples = tuple,
equalityN: Equality = equality,
conditionN: Conditions = condition,
unknownValueN: UnknownValues = unknownValue,
typeEncodingN: TypeEncoding = typeEncoding,
defaultEncodingN: DefaultEncoding = defaultEncoding,
initialFreshCounterValueN: Int = internalFreshNames.getValue
): Context = copy(
override def update(
fieldN: Fields,
arrayN: Arrays,
seqToSetN: SeqToSet,
seqToMultisetN: SeqToMultiset,
seqMultiplicityN: SeqMultiplicity,
optionN: Options,
optionToSeqN: OptionToSeq,
sliceN: Slices,
fixpointN: Fixpoint,
tupleN: Tuples,
equalityN: Equality,
conditionN: Conditions,
unknownValueN: UnknownValues,
typeEncodingN: TypeEncoding,
defaultEncodingN: DefaultEncoding,
initialFreshCounterValueN: Option[Int],
): Context = new ContextImpl(
fieldN,
arrayN,
seqToSetN,
Expand All @@ -97,14 +97,19 @@ case class ContextImpl(
unknownValueN,
typeEncodingN,
defaultEncodingN,
initialFreshCounterValue = initialFreshCounterValueN
table,
initialFreshCounterValueN match {
case None => internalFreshNames
case Some(n) => ContextImpl.FreshNameIteratorImpl(n)
},
)

override def addVars(vars: LocalVarDecl*): Context = this

override val internalFreshNames: FreshNameIteratorImpl = FreshNameIteratorImpl(initialFreshCounterValue)
}

case class FreshNameIteratorImpl(private val initialValue: Int) extends FreshNameIterator {
object ContextImpl {
case class FreshNameIteratorImpl(private val initialValue: Int) extends Context.FreshNameIterator {
private var currentValue: Int = initialValue

override def hasNext: Boolean = true
Expand All @@ -117,5 +122,4 @@ case class ContextImpl(

override def getValue: Int = currentValue
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,6 @@ class PointerEncoding extends LeafTypeEncoding {
* Ref[*e] -> [e]
*/
override def reference(ctx: Context): in.Location ==> CodeWriter[vpr.Exp] = default(super.reference(ctx)){
case (loc: in.Deref) :: _ / Shared =>
ctx.expression(loc.exp)
case (loc: in.Deref) :: _ / Shared => ctx.expression(loc.exp)
}
}
Loading
Loading