Skip to content
Draft
Original file line number Diff line number Diff line change
Expand Up @@ -594,6 +594,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
13 changes: 12 additions & 1 deletion src/main/scala/viper/gobra/ast/internal/Program.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1001,7 +1001,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 @@ -95,7 +95,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 @@ -483,9 +483,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
32 changes: 28 additions & 4 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
}

}
87 changes: 45 additions & 42 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,18 @@ 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 +121,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)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ class ArrayEncoding extends TypeEncoding with SharedArrayEmbedding {
* R[ mset(e: [n]T) ] -> seqToMultiset(ex_array_toSeq([e]))
* R[ x in (e: [n]T) ] -> [x] in ex_array_toSeq([e])
* R[ x # (e: [n]T) ] -> [x] # ex_array_toSeq([e])
* R[ loc: ([n]T)@ ] -> arrayConversion(L[loc])
* R[ loc: ([n]T)@ ] -> arrayConversion(Ref[loc]) // assert [&loc != nil] if [n]T has size zero
*/
override def expression(ctx: Context): in.Expr ==> CodeWriter[vpr.Exp] = default(super.expression(ctx)){
case (loc@ in.IndexedExp(base :: ctx.Array(len, t), idx, _)) :: _ / Exclusive =>
Expand Down Expand Up @@ -175,7 +175,7 @@ class ArrayEncoding extends TypeEncoding with SharedArrayEmbedding {
case n@ in.Length(e :: ctx.Array(len, t) / m) =>
m match {
case Exclusive => ctx.expression(e).map(ex.length(_, cptParam(len, t)(ctx))(n)(ctx))
case Shared => ctx.reference(e.asInstanceOf[in.Location]).map(sh.length(_, cptParam(len, t)(ctx))(n)(ctx))
case Shared => ctx.safeReference(e.asInstanceOf[in.Location]).map(sh.length(_, cptParam(len, t)(ctx))(n)(ctx))
}

case in.Length(exp :: ctx.*(t: in.ArrayT)) =>
Expand Down Expand Up @@ -221,7 +221,7 @@ class ArrayEncoding extends TypeEncoding with SharedArrayEmbedding {
vE <- ctx.expression(e)
} yield ctx.seqMultiplicity.create(vX, ex.toSeq(vE, cptParam(len, t)(ctx))(n)(ctx))(pos, info, errT)

case (loc: in.Location) :: ctx.Array(len, t) / Shared =>
case (loc: in.Location) :: ctx.NoZeroSize(ctx.Array(len, t)) / Shared =>
val (pos, info, errT) = loc.vprMeta
for {
arg <- ctx.reference(loc)
Expand Down Expand Up @@ -260,12 +260,15 @@ class ArrayEncoding extends TypeEncoding with SharedArrayEmbedding {
* i.e. all permissions involved in converting the shared location to an exclusive r-value.
* An encoding for type T should be defined at all shared locations of type T.
*
* The default implements:
* Footprint[loc: T@ if sizeOf(T) == 0] -> [&loc != nil: *T°]
*
* Footprint[loc: [n]T] -> forall idx :: {trigger} 0 <= idx < n ==> Footprint[ loc[idx] ]
* where trigger = sh_array_get(Ref[loc], idx, n)
*
* We do not use let because (at the moment) Viper does not accept quantified permissions with let expressions.
*/
override def addressFootprint(ctx: Context): (in.Location, in.Expr) ==> CodeWriter[vpr.Exp] = {
override def addressFootprint(ctx: Context): (in.Location, in.Expr) ==> CodeWriter[vpr.Exp] = super.addressFootprint(ctx).orElse {
case (loc :: ctx.Array(len, t) / Shared, perm) =>
val (pos, info, errT) = loc.vprMeta
val typ = underlyingType(loc.typ)(ctx)
Expand Down Expand Up @@ -311,13 +314,16 @@ class ArrayEncoding extends TypeEncoding with SharedArrayEmbedding {
* */
private val conversionFunc: FunctionGenerator[ComponentParameter] = new FunctionGenerator[ComponentParameter]{
def genFunction(t: ComponentParameter)(ctx: Context): vpr.Function = {
val info = Source.Parser.Internal

val argType = t.arrayT(Shared)
// variable name does not matter because it is the only argument
val x = in.LocalVar("x", argType)(Source.Parser.Internal)
val x = in.LocalVar("x", argType)(info)

val resultType = argType.withAddressability(Exclusive)
val vResultType = typ(ctx)(resultType)
// variable name does not matter because it is turned into a vpr.Result
val resultVar = in.LocalVar("res", resultType)(Source.Parser.Internal)
val resultVar = in.LocalVar("res", resultType)(info)
val post = pure(equal(ctx)(x, resultVar, x))(ctx).res
// replace resultVar with vpr.Result
.transform{ case v: vpr.LocalVar if v.name == resultVar.id => vpr.Result(vResultType)() }
Expand All @@ -327,7 +333,7 @@ class ArrayEncoding extends TypeEncoding with SharedArrayEmbedding {
formalArgs = Vector(variable(ctx)(x)),
typ = vResultType,
pres = Vector(
pure(addressFootprint(ctx)(x, in.WildcardPerm(Source.Parser.Internal)))(ctx).res,
pure(addressFootprint(ctx)(x, in.WildcardPerm(info)))(ctx).res,
synthesized(termination.DecreasesWildcard(None))("This function is assumed to terminate")
),
posts = Vector(post),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ package viper.gobra.translator.encodings.closures

import viper.gobra.ast.{internal => in}
import viper.gobra.reporting.BackTranslator.{ErrorTransformer, RichErrorMessage}
import viper.gobra.reporting.{InterfaceReceiverIsNilReason, MethodObjectGetterPreconditionError, Source}
import viper.gobra.reporting.{ReceiverIsNilReason, MethodObjectGetterPreconditionError, Source}
import viper.gobra.translator.Names
import viper.gobra.translator.context.Context
import viper.gobra.translator.encodings.interfaces.{InterfaceComponent, InterfaceComponentImpl, InterfaceUtils, PolymorphValueComponent, PolymorphValueComponentImpl, TypeComponent, TypeComponentImpl}
Expand Down Expand Up @@ -105,6 +105,6 @@ class MethodObjectEncoder(domain: ClosureDomainEncoder) {
case e@vprerr.PreconditionInAppFalse(_, _, _) if e causedBy call=>
val info = m.vprMeta._2.asInstanceOf[Source.Verifier.Info]
val recvInfo = m.recv.vprMeta._2.asInstanceOf[Source.Verifier.Info]
MethodObjectGetterPreconditionError(info).dueTo(InterfaceReceiverIsNilReason(recvInfo))
MethodObjectGetterPreconditionError(info).dueTo(ReceiverIsNilReason(recvInfo))
}
}
Loading
Loading