From d52394f1c782cb5d9ab44c9624ab9207f6ededb0 Mon Sep 17 00:00:00 2001 From: Alexander Stekelenburg Date: Tue, 14 May 2024 16:30:42 +0200 Subject: [PATCH 01/26] Create separate classes for by reference and by value classes --- src/col/vct/col/ast/Node.scala | 59 ++++- .../global/ByReferenceClassImpl.scala | 10 + .../declaration/global/ByValueClassImpl.scala | 12 + .../ast/declaration/global/ClassImpl.scala | 28 +- .../declaration/singular/EndpointImpl.scala | 4 +- .../singular/LocalHeapVariableImpl.scala | 18 ++ .../expr/ambiguous/AmbiguousPlusImpl.scala | 14 +- .../ast/expr/context/AmbiguousThisImpl.scala | 5 +- .../col/ast/expr/context/ThisObjectImpl.scala | 4 +- .../ast/expr/heap/alloc/NewObjectImpl.scala | 4 +- .../vct/col/ast/expr/misc/HeapLocalImpl.scala | 17 ++ .../family/coercion/CoerceNullClassImpl.scala | 2 +- .../family/coercion/CoerceSupportsImpl.scala | 4 +- .../nonexecutable/HeapLocalDeclImpl.scala | 14 + .../col/ast/type/TByReferenceClassImpl.scala | 8 + .../vct/col/ast/type/TByValueClassImpl.scala | 8 + src/col/vct/col/ast/type/TClassImpl.scala | 24 +- .../col/ast/unsorted/ConstructorImpl.scala | 3 +- .../col/ast/unsorted/PVLEndpointImpl.scala | 2 +- src/col/vct/col/origin/Blame.scala | 21 +- src/col/vct/col/origin/Origin.scala | 3 + src/col/vct/col/resolve/Resolve.scala | 9 +- src/col/vct/col/resolve/lang/Java.scala | 3 +- src/col/vct/col/resolve/lang/PVL.scala | 12 +- src/col/vct/col/resolve/lang/Spec.scala | 24 +- .../vct/col/typerules/CoercingRewriter.scala | 13 +- src/col/vct/col/typerules/CoercionUtils.scala | 19 +- src/col/vct/col/typerules/Types.scala | 4 +- src/col/vct/col/util/AstBuildHelpers.scala | 8 + src/llvm/tools/vcllvm/VCLLVM.cpp | 8 +- src/main/vct/main/stages/Transformation.scala | 2 + .../vct/parsers/transform/PVLToCol.scala | 2 +- .../systemctocol/engine/ClassTransformer.java | 8 +- .../engine/KnownTypeTransformer.java | 10 +- .../systemctocol/engine/MainTransformer.java | 6 +- .../rewrite/CheckContractSatisfiability.scala | 36 +-- .../vct/rewrite/CheckProcessAlgebra.scala | 2 +- src/rewrite/vct/rewrite/ClassToRef.scala | 32 ++- .../vct/rewrite/ConstantifyFinalFields.scala | 22 +- .../vct/rewrite/EncodeArrayValues.scala | 4 +- src/rewrite/vct/rewrite/EncodeAutoValue.scala | 52 ++-- .../vct/rewrite/EncodeByValueClass.scala | 249 ++++++++++++++++++ src/rewrite/vct/rewrite/EncodeForkJoin.scala | 8 +- .../vct/rewrite/EncodeIntrinsicLock.scala | 4 +- .../vct/rewrite/EncodeResourceValues.scala | 22 +- .../ExtractInlineQuantifierPatterns.scala | 72 ++--- .../vct/rewrite/MonomorphizeClass.scala | 64 +++-- .../MonomorphizeContractApplicables.scala | 8 +- src/rewrite/vct/rewrite/ParBlockEncoder.scala | 48 ++-- .../ResolveExpressionSideEffects.scala | 33 ++- src/rewrite/vct/rewrite/adt/ImportADT.scala | 2 +- src/rewrite/vct/rewrite/bip/EncodeBip.scala | 4 +- src/rewrite/vct/rewrite/cfg/Utils.scala | 6 +- .../vct/rewrite/exc/EncodeBreakReturn.scala | 22 +- .../vct/rewrite/lang/LangCPPToCol.scala | 45 ++-- src/rewrite/vct/rewrite/lang/LangCToCol.scala | 114 +------- .../vct/rewrite/lang/LangJavaToCol.scala | 10 +- .../vct/rewrite/lang/LangPVLToCol.scala | 2 +- .../vct/rewrite/lang/LangSpecificToCol.scala | 12 +- .../vct/rewrite/lang/LangTypesToCol.scala | 5 +- .../vct/rewrite/lang/NoSupportSelfLoop.scala | 10 +- src/rewrite/vct/rewrite/util/Extract.scala | 4 +- .../vct/rewrite/veymont/EncodeChannels.scala | 16 +- .../rewrite/veymont/EncodeChoreography.scala | 8 +- .../EncodeChoreographyParameters.scala | 10 +- .../GenerateChoreographyPermissions.scala | 13 +- .../veymont/GenerateImplementation.scala | 18 +- .../veymont/SpecializeEndpointClasses.scala | 3 +- .../vct/helper/SimpleProgramGenerator.scala | 2 +- .../vct/test/integration/examples/CSpec.scala | 4 +- 70 files changed, 937 insertions(+), 421 deletions(-) create mode 100644 src/col/vct/col/ast/declaration/global/ByReferenceClassImpl.scala create mode 100644 src/col/vct/col/ast/declaration/global/ByValueClassImpl.scala create mode 100644 src/col/vct/col/ast/declaration/singular/LocalHeapVariableImpl.scala create mode 100644 src/col/vct/col/ast/expr/misc/HeapLocalImpl.scala create mode 100644 src/col/vct/col/ast/statement/nonexecutable/HeapLocalDeclImpl.scala create mode 100644 src/col/vct/col/ast/type/TByReferenceClassImpl.scala create mode 100644 src/col/vct/col/ast/type/TByValueClassImpl.scala create mode 100644 src/rewrite/vct/rewrite/EncodeByValueClass.scala diff --git a/src/col/vct/col/ast/Node.scala b/src/col/vct/col/ast/Node.scala index 838740745f..67e3e06e5a 100644 --- a/src/col/vct/col/ast/Node.scala +++ b/src/col/vct/col/ast/Node.scala @@ -107,6 +107,7 @@ final case class VerificationContext[G](program: Program[G])( @scopes[ModelDeclaration] @scopes[EnumConstant] @scopes[Variable] +@scopes[LocalHeapVariable] final case class Program[G](declarations: Seq[GlobalDeclaration[G]])( val blame: Blame[UnsafeCoercion] )(implicit val o: Origin) @@ -218,9 +219,17 @@ sealed trait DeclaredType[G] extends Type[G] with DeclaredTypeImpl[G] final case class TModel[G](model: Ref[G, Model[G]])( implicit val o: Origin = DiagnosticOrigin ) extends DeclaredType[G] with TModelImpl[G] -final case class TClass[G](cls: Ref[G, Class[G]], typeArgs: Seq[Type[G]])( - implicit val o: Origin = DiagnosticOrigin -) extends DeclaredType[G] with TClassImpl[G] +sealed trait TClass[G] extends DeclaredType[G] with TClassImpl[G] +final case class TByReferenceClass[G]( + cls: Ref[G, Class[G]], + typeArgs: Seq[Type[G]], +)(implicit val o: Origin = DiagnosticOrigin) + extends TClass[G] with TByReferenceClassImpl[G] +final case class TByValueClass[G]( + cls: Ref[G, Class[G]], + typeArgs: Seq[Type[G]], +)(implicit val o: Origin = DiagnosticOrigin) + extends TClass[G] with TByValueClassImpl[G] final case class TAnyClass[G]()(implicit val o: Origin = DiagnosticOrigin) extends DeclaredType[G] with TAnyClassImpl[G] final case class TAxiomatic[G]( @@ -249,6 +258,7 @@ final case class ParSequential[G](regions: Seq[ParRegion[G]])( )(implicit val o: Origin) extends ParRegion[G] with ParSequentialImpl[G] @scopes[Variable] +@scopes[LocalHeapVariable] @scopes[SendDecl] @scopes[ParBlockDecl] final case class ParBlock[G]( @@ -276,6 +286,7 @@ final case class IterationContract[G]( extends LoopContract[G] with IterationContractImpl[G] @family @scopes[Variable] +@scopes[LocalHeapVariable] final case class CatchClause[G](decl: Variable[G], body: Statement[G])( implicit val o: Origin ) extends NodeFamily[G] with CatchClauseImpl[G] @@ -314,6 +325,10 @@ final case class LocalDecl[G](local: Variable[G])(implicit val o: Origin) extends NonExecutableStatement[G] with PurelySequentialStatement[G] with LocalDeclImpl[G] +final case class HeapLocalDecl[G](local: LocalHeapVariable[G])(implicit val o: Origin) + extends NonExecutableStatement[G] + with PurelySequentialStatement[G] + with HeapLocalDeclImpl[G] final case class SpecIgnoreStart[G]()(implicit val o: Origin) extends NonExecutableStatement[G] with PurelySequentialStatement[G] @@ -520,6 +535,7 @@ final case class Block[G](statements: Seq[Statement[G]])(implicit val o: Origin) with ControlContainerStatement[G] with BlockImpl[G] @scopes[Variable] +@scopes[LocalHeapVariable] @scopes[CLocalDeclaration] @scopes[CPPLocalDeclaration] @scopes[JavaLocalDeclaration] @@ -555,6 +571,7 @@ final case class Loop[G]( with ControlContainerStatement[G] with LoopImpl[G] @scopes[Variable] +@scopes[LocalHeapVariable] final case class RangedFor[G]( iter: IterVariable[G], contract: LoopContract[G], @@ -609,6 +626,7 @@ final case class ParStatement[G](impl: ParRegion[G])(implicit val o: Origin) with PurelySequentialStatement[G] with ParStatementImpl[G] @scopes[Variable] +@scopes[LocalHeapVariable] final case class VecBlock[G]( iters: Seq[IterVariable[G]], requires: Expr[G], @@ -643,18 +661,26 @@ final class HeapVariable[G](val t: Type[G])(implicit val o: Origin) final class SimplificationRule[G](val axiom: Expr[G])(implicit val o: Origin) extends GlobalDeclaration[G] with SimplificationRuleImpl[G] @scopes[Variable] +@scopes[LocalHeapVariable] final class AxiomaticDataType[G]( val decls: Seq[ADTDeclaration[G]], val typeArgs: Seq[Variable[G]], )(implicit val o: Origin) extends GlobalDeclaration[G] with AxiomaticDataTypeImpl[G] -final class Class[G]( +sealed trait Class[G] extends GlobalDeclaration[G] with ClassImpl[G] +final class ByReferenceClass[G]( val typeArgs: Seq[Variable[G]], val decls: Seq[ClassDeclaration[G]], val supports: Seq[Type[G]], val intrinsicLockInvariant: Expr[G], )(implicit val o: Origin) - extends GlobalDeclaration[G] with ClassImpl[G] + extends Class[G] with ByReferenceClassImpl[G] +final class ByValueClass[G]( + val typeArgs: Seq[Variable[G]], + val decls: Seq[ClassDeclaration[G]], + val supports: Seq[Type[G]], +)(implicit val o: Origin) + extends Class[G] with ByValueClassImpl[G] final class Model[G](val declarations: Seq[ModelDeclaration[G]])( implicit val o: Origin ) extends GlobalDeclaration[G] with Declarator[G] with ModelImpl[G] @@ -687,6 +713,7 @@ final class VeSUVMainMethod[G](val body: Option[Statement[G]])( )(implicit val o: Origin) extends GlobalDeclaration[G] with VeSUVMainMethodImpl[G] @scopes[Variable] +@scopes[LocalHeapVariable] final class Predicate[G]( val args: Seq[Variable[G]], val body: Option[Expr[G]], @@ -759,6 +786,7 @@ final class InstanceMethod[G]( with AbstractMethod[G] with InstanceMethodImpl[G] @scopes[Variable] +@scopes[LocalHeapVariable] final class InstancePredicate[G]( val args: Seq[Variable[G]], val body: Option[Expr[G]], @@ -814,6 +842,7 @@ sealed trait ModelDeclaration[G] final class ModelField[G](val t: Type[G])(implicit val o: Origin) extends ModelDeclaration[G] with Field[G] with ModelFieldImpl[G] @scopes[Variable] +@scopes[LocalHeapVariable] final class ModelProcess[G]( val args: Seq[Variable[G]], val impl: Expr[G], @@ -824,6 +853,7 @@ final class ModelProcess[G]( )(val blame: Blame[PostconditionFailed])(implicit val o: Origin) extends ModelDeclaration[G] with Applicable[G] with ModelProcessImpl[G] @scopes[Variable] +@scopes[LocalHeapVariable] final class ModelAction[G]( val args: Seq[Variable[G]], val requires: Expr[G], @@ -838,6 +868,7 @@ sealed trait ADTDeclaration[G] extends Declaration[G] with ADTDeclarationImpl[G] final class ADTAxiom[G](val axiom: Expr[G])(implicit val o: Origin) extends ADTDeclaration[G] with ADTAxiomImpl[G] @scopes[Variable] +@scopes[LocalHeapVariable] final class ADTFunction[G](val args: Seq[Variable[G]], val returnType: Type[G])( implicit val o: Origin ) extends Applicable[G] with ADTDeclaration[G] with ADTFunctionImpl[G] @@ -846,6 +877,9 @@ final class ADTFunction[G](val args: Seq[Variable[G]], val returnType: Type[G])( final class Variable[G](val t: Type[G])(implicit val o: Origin) extends Declaration[G] with VariableImpl[G] @family +final class LocalHeapVariable[G](val t: Type[G])(implicit val o: Origin) + extends Declaration[G] with LocalHeapVariableImpl[G] +@family final class LabelDecl[G]()(implicit val o: Origin) extends Declaration[G] with LabelDeclImpl[G] @family @@ -872,6 +906,7 @@ sealed trait AbstractMethod[G] sealed trait Field[G] extends FieldImpl[G] @family @scopes[Variable] +@scopes[LocalHeapVariable] final case class SignalsClause[G](binding: Variable[G], assn: Expr[G])( implicit val o: Origin ) extends NodeFamily[G] with SignalsClauseImpl[G] @@ -1260,6 +1295,7 @@ final case class MapRemove[G](map: Expr[G], k: Expr[G])(implicit val o: Origin) sealed trait Binder[G] extends Expr[G] with BinderImpl[G] @scopes[Variable] +@scopes[LocalHeapVariable] final case class Forall[G]( bindings: Seq[Variable[G]], triggers: Seq[Seq[Expr[G]]], @@ -1267,6 +1303,7 @@ final case class Forall[G]( )(implicit val o: Origin) extends Binder[G] with ForallImpl[G] @scopes[Variable] +@scopes[LocalHeapVariable] final case class Starall[G]( bindings: Seq[Variable[G]], triggers: Seq[Seq[Expr[G]]], @@ -1274,6 +1311,7 @@ final case class Starall[G]( )(val blame: Blame[ReceiverNotInjective])(implicit val o: Origin) extends Binder[G] with StarallImpl[G] @scopes[Variable] +@scopes[LocalHeapVariable] final case class Exists[G]( bindings: Seq[Variable[G]], triggers: Seq[Seq[Expr[G]]], @@ -1281,6 +1319,7 @@ final case class Exists[G]( )(implicit val o: Origin) extends Binder[G] with ExistsImpl[G] @scopes[Variable] +@scopes[LocalHeapVariable] final case class Sum[G]( bindings: Seq[Variable[G]], condition: Expr[G], @@ -1288,6 +1327,7 @@ final case class Sum[G]( )(implicit val o: Origin) extends Binder[G] with SumImpl[G] @scopes[Variable] +@scopes[LocalHeapVariable] final case class Product[G]( bindings: Seq[Variable[G]], condition: Expr[G], @@ -1295,6 +1335,7 @@ final case class Product[G]( )(implicit val o: Origin) extends Binder[G] with ProductImpl[G] @scopes[Variable] +@scopes[LocalHeapVariable] final case class ForPerm[G]( bindings: Seq[Variable[G]], loc: Location[G], @@ -1302,10 +1343,12 @@ final case class ForPerm[G]( )(implicit val o: Origin) extends Binder[G] with ForPermImpl[G] @scopes[Variable] +@scopes[LocalHeapVariable] final case class ForPermWithValue[G](binding: Variable[G], body: Expr[G])( implicit val o: Origin ) extends Binder[G] with ForPermWithValueImpl[G] @scopes[Variable] +@scopes[LocalHeapVariable] final case class Let[G](binding: Variable[G], value: Expr[G], main: Expr[G])( implicit val o: Origin ) extends Binder[G] with LetImpl[G] @@ -1317,12 +1360,16 @@ final case class InlinePattern[G]( extends Expr[G] with InlinePatternImpl[G] @scopes[Variable] +@scopes[LocalHeapVariable] final case class ScopedExpr[G](declarations: Seq[Variable[G]], body: Expr[G])( implicit val o: Origin ) extends Declarator[G] with Expr[G] with ScopedExprImpl[G] final case class Local[G](ref: Ref[G, Variable[G]])(implicit val o: Origin) extends Expr[G] with LocalImpl[G] +final case class HeapLocal[G](ref: Ref[G, LocalHeapVariable[G]])(implicit val o: Origin) + extends Expr[G] with HeapLocalImpl[G] + final case class EnumUse[G]( enum: Ref[G, Enum[G]], const: Ref[G, EnumConstant[G]], @@ -3106,6 +3153,7 @@ sealed trait JavaClassOrInterface[G] with Declarator[G] with JavaClassOrInterfaceImpl[G] @scopes[Variable] +@scopes[LocalHeapVariable] final class JavaClass[G]( val name: String, val modifiers: Seq[JavaModifier[G]], @@ -3117,6 +3165,7 @@ final class JavaClass[G]( )(val blame: Blame[JavaImplicitConstructorFailure])(implicit val o: Origin) extends JavaClassOrInterface[G] with JavaClassImpl[G] @scopes[Variable] +@scopes[LocalHeapVariable] final class JavaInterface[G]( val name: String, val modifiers: Seq[JavaModifier[G]], diff --git a/src/col/vct/col/ast/declaration/global/ByReferenceClassImpl.scala b/src/col/vct/col/ast/declaration/global/ByReferenceClassImpl.scala new file mode 100644 index 0000000000..ec95adbe7c --- /dev/null +++ b/src/col/vct/col/ast/declaration/global/ByReferenceClassImpl.scala @@ -0,0 +1,10 @@ +package vct.col.ast.declaration.global + +import vct.col.ast.{ByReferenceClass, TByReferenceClass, Type} +import vct.col.ast.ops.ByReferenceClassOps + +trait ByReferenceClassImpl[G] extends ByReferenceClassOps[G] { + this: ByReferenceClass[G] => + override def classType(typeArgs: Seq[Type[G]]): TByReferenceClass[G] = + TByReferenceClass[G](this.ref, typeArgs) +} diff --git a/src/col/vct/col/ast/declaration/global/ByValueClassImpl.scala b/src/col/vct/col/ast/declaration/global/ByValueClassImpl.scala new file mode 100644 index 0000000000..e776781309 --- /dev/null +++ b/src/col/vct/col/ast/declaration/global/ByValueClassImpl.scala @@ -0,0 +1,12 @@ +package vct.col.ast.declaration.global + +import vct.col.ast.{ByValueClass, Expr, InstanceField, TByValueClass, Type} +import vct.col.ast.ops.ByValueClassOps +import vct.col.util.AstBuildHelpers._ + +trait ByValueClassImpl[G] extends ByValueClassOps[G] { + this: ByValueClass[G] => + override def intrinsicLockInvariant: Expr[G] = tt + override def classType(typeArgs: Seq[Type[G]]): TByValueClass[G] = + TByValueClass[G](this.ref, typeArgs) +} diff --git a/src/col/vct/col/ast/declaration/global/ClassImpl.scala b/src/col/vct/col/ast/declaration/global/ClassImpl.scala index 6b26f4e42b..7320c8dadf 100644 --- a/src/col/vct/col/ast/declaration/global/ClassImpl.scala +++ b/src/col/vct/col/ast/declaration/global/ClassImpl.scala @@ -1,18 +1,36 @@ package vct.col.ast.declaration.global -import vct.col.ast.{Class, Declaration, TClass, TVar} +import vct.col.ast.{ + Class, + ClassDeclaration, + Declaration, + Expr, + TByReferenceClass, + TClass, + TVar, + Type, + Variable, +} import vct.col.ast.util.Declarator import vct.col.print._ import vct.col.util.AstBuildHelpers.tt -import vct.result.VerificationError.Unreachable -import vct.col.ast.ops.ClassOps -trait ClassImpl[G] extends Declarator[G] with ClassOps[G] { +trait ClassImpl[G] extends Declarator[G] { this: Class[G] => + def typeArgs: Seq[Variable[G]] + def decls: Seq[ClassDeclaration[G]] + def supports: Seq[Type[G]] + def intrinsicLockInvariant: Expr[G] + + def classType(typeArgs: Seq[Type[G]]): TClass[G] + def transSupportArrowsHelper( seen: Set[TClass[G]] ): Seq[(TClass[G], TClass[G])] = { - val t: TClass[G] = TClass(this.ref, typeArgs.map(v => TVar(v.ref))) + // TODO: Does this break things if we have a ByValueClass with supers? + val t: TClass[G] = classType( + typeArgs.map((v: Variable[G]) => TVar(v.ref[Variable[G]])) + ) if (seen.contains(t)) Nil else diff --git a/src/col/vct/col/ast/declaration/singular/EndpointImpl.scala b/src/col/vct/col/ast/declaration/singular/EndpointImpl.scala index 1d2c5b607f..1d236b23f1 100644 --- a/src/col/vct/col/ast/declaration/singular/EndpointImpl.scala +++ b/src/col/vct/col/ast/declaration/singular/EndpointImpl.scala @@ -1,7 +1,7 @@ package vct.col.ast.declaration.singular import vct.col.ast.declaration.DeclarationImpl -import vct.col.ast.{Endpoint, TClass, Type} +import vct.col.ast.{Endpoint, TByReferenceClass, TClass, Type} import vct.col.print._ import vct.col.ast.ops.{EndpointFamilyOps, EndpointOps} import vct.col.check.{CheckContext, CheckError} @@ -14,7 +14,7 @@ trait EndpointImpl[G] Group(t.show <> "(" <> Doc.args(args) <> ");") }) - def t: TClass[G] = TClass(cls, typeArgs) + def t: TClass[G] = TByReferenceClass(cls, typeArgs) override def check(ctx: CheckContext[G]): Seq[CheckError] = super.check(ctx) ++ ctx.checkInScope(this, cls) diff --git a/src/col/vct/col/ast/declaration/singular/LocalHeapVariableImpl.scala b/src/col/vct/col/ast/declaration/singular/LocalHeapVariableImpl.scala new file mode 100644 index 0000000000..2814eed660 --- /dev/null +++ b/src/col/vct/col/ast/declaration/singular/LocalHeapVariableImpl.scala @@ -0,0 +1,18 @@ +package vct.col.ast.declaration.singular + +import vct.col.ast.LocalHeapVariable +import vct.col.print._ +import vct.col.ast.ops.{LocalHeapVariableOps, VariableFamilyOps} +import vct.col.ast.ops.{LocalHeapVariableOps, LocalHeapVariableFamilyOps} + +trait LocalHeapVariableImpl[G] extends LocalHeapVariableOps[G] with LocalHeapVariableFamilyOps[G] { + this: LocalHeapVariable[G] => + override def layout(implicit ctx: Ctx): Doc = + Text("@heap") <+> (ctx.syntax match { + case Ctx.C | Ctx.Cuda | Ctx.OpenCL | Ctx.CPP => + val (spec, decl) = t.layoutSplitDeclarator + spec <+> decl <> ctx.name(this) + case Ctx.PVL | Ctx.Java => t.show <+> ctx.name(this) + case Ctx.Silver => Text(ctx.name(this)) <> ":" <+> t + }) +} diff --git a/src/col/vct/col/ast/expr/ambiguous/AmbiguousPlusImpl.scala b/src/col/vct/col/ast/expr/ambiguous/AmbiguousPlusImpl.scala index 8a227e7898..73233d9105 100644 --- a/src/col/vct/col/ast/expr/ambiguous/AmbiguousPlusImpl.scala +++ b/src/col/vct/col/ast/expr/ambiguous/AmbiguousPlusImpl.scala @@ -20,7 +20,7 @@ trait AmbiguousPlusImpl[G] extends AmbiguousPlusOps[G] { right val decls = subject.t match { - case TClass(Ref(cls), _) => cls.decls + case t: TClass[G] => t.cls.decl.decls case JavaTClass(Ref(cls), _) => cls.decls case _ => return None } @@ -62,6 +62,18 @@ trait AmbiguousPlusImpl[G] extends AmbiguousPlusOps[G] { getCustomPlusOpType().get else getNumericType + if (isProcessOp) + TProcess() + else if (isSeqOp || isBagOp || isSetOp || isVectorOp) + Types.leastCommonSuperType(left.t, right.t) + else if (isPointerOp) + left.t + else if (isStringOp) + TString() + else if (getCustomPlusOpType().isDefined) + getCustomPlusOpType().get + else + getNumericType } override def precedence: Int = Precedence.ADDITIVE diff --git a/src/col/vct/col/ast/expr/context/AmbiguousThisImpl.scala b/src/col/vct/col/ast/expr/context/AmbiguousThisImpl.scala index 01b0ab843c..18cad16207 100644 --- a/src/col/vct/col/ast/expr/context/AmbiguousThisImpl.scala +++ b/src/col/vct/col/ast/expr/context/AmbiguousThisImpl.scala @@ -17,9 +17,8 @@ trait AmbiguousThisImpl[G] extends AmbiguousThisOps[G] { ) match { case RefJavaClass(decl) => JavaTClass(decl.ref, Nil) case RefClass(decl) => - TClass( - decl.ref, - decl.typeArgs.map((v: Variable[G]) => TVar(v.ref[Variable[G]])), + decl.classType( + decl.typeArgs.map((v: Variable[G]) => TVar(v.ref[Variable[G]])) ) case RefModel(decl) => TModel(decl.ref) case RefPVLChoreography(decl) => TPVLChoreography(decl.ref) diff --git a/src/col/vct/col/ast/expr/context/ThisObjectImpl.scala b/src/col/vct/col/ast/expr/context/ThisObjectImpl.scala index 25e90414f7..74a515cf8d 100644 --- a/src/col/vct/col/ast/expr/context/ThisObjectImpl.scala +++ b/src/col/vct/col/ast/expr/context/ThisObjectImpl.scala @@ -8,7 +8,9 @@ import vct.col.check.{CheckContext, CheckError, ThisInConstructorPre} trait ThisObjectImpl[G] extends ThisDeclarationImpl[G] with ThisObjectOps[G] { this: ThisObject[G] => override def t: Type[G] = - TClass(cls, cls.decl.typeArgs.map(v => TVar(v.ref[Variable[G]]))) + cls.decl.classType(cls.decl.typeArgs.map((v: Variable[G]) => + TVar(v.ref[Variable[G]]) + )) override def check(context: CheckContext[G]): Seq[CheckError] = { val inConstructor = diff --git a/src/col/vct/col/ast/expr/heap/alloc/NewObjectImpl.scala b/src/col/vct/col/ast/expr/heap/alloc/NewObjectImpl.scala index dbc0fa2953..f5e8ead476 100644 --- a/src/col/vct/col/ast/expr/heap/alloc/NewObjectImpl.scala +++ b/src/col/vct/col/ast/expr/heap/alloc/NewObjectImpl.scala @@ -1,12 +1,12 @@ package vct.col.ast.expr.heap.alloc -import vct.col.ast.{NewObject, TClass, Type} +import vct.col.ast.{NewObject, Type} import vct.col.print.{Ctx, Doc, Precedence, Text} import vct.col.ast.ops.NewObjectOps trait NewObjectImpl[G] extends NewObjectOps[G] { this: NewObject[G] => - override def t: Type[G] = TClass(cls, Seq()) + override def t: Type[G] = cls.decl.classType(Seq()) override def precedence: Int = Precedence.POSTFIX override def layout(implicit ctx: Ctx): Doc = diff --git a/src/col/vct/col/ast/expr/misc/HeapLocalImpl.scala b/src/col/vct/col/ast/expr/misc/HeapLocalImpl.scala new file mode 100644 index 0000000000..d397452dc6 --- /dev/null +++ b/src/col/vct/col/ast/expr/misc/HeapLocalImpl.scala @@ -0,0 +1,17 @@ +package vct.col.ast.expr.misc + +import vct.col.ast.expr.ExprImpl +import vct.col.ast.{HeapLocal, Type} +import vct.col.check.{CheckContext, CheckError} +import vct.col.print.{Ctx, Doc, Precedence, Text} +import vct.col.ast.ops.HeapLocalOps + +trait HeapLocalImpl[G] extends ExprImpl[G] with HeapLocalOps[G] { + this: HeapLocal[G] => + override def t: Type[G] = ref.decl.t + override def check(context: CheckContext[G]): Seq[CheckError] = + context.checkInScope(this, ref) + + override def precedence: Int = Precedence.ATOMIC + override def layout(implicit ctx: Ctx): Doc = Text(ctx.name(ref)) +} diff --git a/src/col/vct/col/ast/family/coercion/CoerceNullClassImpl.scala b/src/col/vct/col/ast/family/coercion/CoerceNullClassImpl.scala index 4dc7f0662b..01b76c7319 100644 --- a/src/col/vct/col/ast/family/coercion/CoerceNullClassImpl.scala +++ b/src/col/vct/col/ast/family/coercion/CoerceNullClassImpl.scala @@ -5,5 +5,5 @@ import vct.col.ast.ops.CoerceNullClassOps trait CoerceNullClassImpl[G] extends CoerceNullClassOps[G] { this: CoerceNullClass[G] => - override def target: TClass[G] = TClass(targetClass, typeArgs) + override def target: TClass[G] = targetClass.decl.classType(typeArgs) } diff --git a/src/col/vct/col/ast/family/coercion/CoerceSupportsImpl.scala b/src/col/vct/col/ast/family/coercion/CoerceSupportsImpl.scala index e0ce3a4353..0a04b3f6ce 100644 --- a/src/col/vct/col/ast/family/coercion/CoerceSupportsImpl.scala +++ b/src/col/vct/col/ast/family/coercion/CoerceSupportsImpl.scala @@ -7,5 +7,7 @@ trait CoerceSupportsImpl[G] extends CoerceSupportsOps[G] { this: CoerceSupports[G] => // TODO (RR): Integrate coercions with generics? override def target: TClass[G] = - TClass(targetClass, { assert(sourceClass.decl.typeArgs.isEmpty); Seq() }) + targetClass.decl.classType({ + assert(sourceClass.decl.typeArgs.isEmpty); Seq() + }) } diff --git a/src/col/vct/col/ast/statement/nonexecutable/HeapLocalDeclImpl.scala b/src/col/vct/col/ast/statement/nonexecutable/HeapLocalDeclImpl.scala new file mode 100644 index 0000000000..2b7e8650fd --- /dev/null +++ b/src/col/vct/col/ast/statement/nonexecutable/HeapLocalDeclImpl.scala @@ -0,0 +1,14 @@ +package vct.col.ast.statement.nonexecutable + +import vct.col.ast.HeapLocalDecl +import vct.col.print.{Ctx, Doc, Text} +import vct.col.ast.ops.HeapLocalDeclOps + +trait HeapLocalDeclImpl[G] extends HeapLocalDeclOps[G] { + this: HeapLocalDecl[G] => + override def layout(implicit ctx: Ctx): Doc = + ctx.syntax match { + case Ctx.Silver => Text("var") <+> local.show + case _ => local.show <> ";" + } +} diff --git a/src/col/vct/col/ast/type/TByReferenceClassImpl.scala b/src/col/vct/col/ast/type/TByReferenceClassImpl.scala new file mode 100644 index 0000000000..8a800321e9 --- /dev/null +++ b/src/col/vct/col/ast/type/TByReferenceClassImpl.scala @@ -0,0 +1,8 @@ +package vct.col.ast.`type` + +import vct.col.ast.TByReferenceClass +import vct.col.ast.ops.TByReferenceClassOps + +trait TByReferenceClassImpl[G] extends TByReferenceClassOps[G] { + this: TByReferenceClass[G] => +} diff --git a/src/col/vct/col/ast/type/TByValueClassImpl.scala b/src/col/vct/col/ast/type/TByValueClassImpl.scala new file mode 100644 index 0000000000..0f8e0f6bdc --- /dev/null +++ b/src/col/vct/col/ast/type/TByValueClassImpl.scala @@ -0,0 +1,8 @@ +package vct.col.ast.`type` + +import vct.col.ast.TByValueClass +import vct.col.ast.ops.TByValueClassOps + +trait TByValueClassImpl[G] extends TByValueClassOps[G] { + this: TByValueClass[G] => +} diff --git a/src/col/vct/col/ast/type/TClassImpl.scala b/src/col/vct/col/ast/type/TClassImpl.scala index 42a03d7983..1fd098e9c2 100644 --- a/src/col/vct/col/ast/type/TClassImpl.scala +++ b/src/col/vct/col/ast/type/TClassImpl.scala @@ -1,27 +1,23 @@ package vct.col.ast.`type` import vct.col.ast.{ - Applicable, Class, - ClassDeclaration, - Constructor, - ContractApplicable, InstanceField, - InstanceFunction, - InstanceMethod, - InstanceOperatorFunction, - InstanceOperatorMethod, + TByReferenceClass, + TByValueClass, TClass, Type, Variable, } -import vct.col.print.{Ctx, Doc, Empty, Group, Text} -import vct.col.ast.ops.TClassOps +import vct.col.print._ import vct.col.ref.Ref -import vct.result.VerificationError.Unreachable -trait TClassImpl[G] extends TClassOps[G] { +trait TClassImpl[G] { this: TClass[G] => + def cls: Ref[G, Class[G]] + + def typeArgs: Seq[Type[G]] + def transSupportArrowsHelper( seen: Set[TClass[G]] ): Seq[(TClass[G], TClass[G])] = @@ -45,7 +41,9 @@ trait TClassImpl[G] extends TClassOps[G] { def instantiate(t: Type[G]): Type[G] = this match { - case TClass(Ref(cls), typeArgs) if typeArgs.nonEmpty => + case TByReferenceClass(Ref(cls), typeArgs) if typeArgs.nonEmpty => + t.particularize(cls.typeArgs.zip(typeArgs).toMap) + case TByValueClass(Ref(cls), typeArgs) if typeArgs.nonEmpty => t.particularize(cls.typeArgs.zip(typeArgs).toMap) case _ => t } diff --git a/src/col/vct/col/ast/unsorted/ConstructorImpl.scala b/src/col/vct/col/ast/unsorted/ConstructorImpl.scala index 23f656fc03..cec2a2bdfc 100644 --- a/src/col/vct/col/ast/unsorted/ConstructorImpl.scala +++ b/src/col/vct/col/ast/unsorted/ConstructorImpl.scala @@ -8,7 +8,7 @@ trait ConstructorImpl[G] extends ConstructorOps[G] { this: Constructor[G] => override def pure: Boolean = false override def returnType: TClass[G] = - TClass(cls, cls.decl.typeArgs.map((v: Variable[G]) => TVar(v.ref))) + cls.decl.classType(cls.decl.typeArgs.map((v: Variable[G]) => TVar(v.ref))) override def layout(implicit ctx: Ctx): Doc = { Doc.stack(Seq( @@ -19,5 +19,4 @@ trait ConstructorImpl[G] extends ConstructorOps[G] { ) <> body.map(Text(" ") <> _).getOrElse(Text(";")), )) } - } diff --git a/src/col/vct/col/ast/unsorted/PVLEndpointImpl.scala b/src/col/vct/col/ast/unsorted/PVLEndpointImpl.scala index 9aea9ecdf0..04d804ee0f 100644 --- a/src/col/vct/col/ast/unsorted/PVLEndpointImpl.scala +++ b/src/col/vct/col/ast/unsorted/PVLEndpointImpl.scala @@ -8,5 +8,5 @@ trait PVLEndpointImpl[G] extends PVLEndpointOps[G] { this: PVLEndpoint[G] => // override def layout(implicit ctx: Ctx): Doc = ??? - def t: TClass[G] = TClass(cls, typeArgs) + def t: TClass[G] = cls.decl.classType(typeArgs) } diff --git a/src/col/vct/col/origin/Blame.scala b/src/col/vct/col/origin/Blame.scala index 068aa2f893..174b70f334 100644 --- a/src/col/vct/col/origin/Blame.scala +++ b/src/col/vct/col/origin/Blame.scala @@ -158,22 +158,25 @@ case class AssignFieldFailed(node: SilverFieldAssign[_]) s"Insufficient permission for assignment `$source`." } -case class CopyStructFailed(node: Expr[_], field: String) +case class CopyClassFailed(node: Node[_], clazz: ByValueClass[_], field: String) extends AssignFailed with NodeVerificationFailure { - override def code: String = "copyStructFailed" + override def code: String = "copyClassFailed" override def descInContext: String = - s"Insufficient read permission for field '$field' to copy struct." + s"Insufficient read permission for field '$field' to copy ${clazz.o + .find[TypeName].map(_.name).getOrElse("class")}." override def inlineDescWithSource(source: String): String = s"Insufficient permission for assignment `$source`." } -case class CopyStructFailedBeforeCall(node: Expr[_], field: String) - extends AssignFailed - with FrontendInvocationError - with NodeVerificationFailure { - override def code: String = "copyStructFailedBeforeCall" +case class CopyClassFailedBeforeCall( + node: Node[_], + clazz: ByValueClass[_], + field: String, +) extends AssignFailed with InvocationFailure with NodeVerificationFailure { + override def code: String = "copyClassFailedBeforeCall" override def descInContext: String = - s"Insufficient read permission for field '$field' to copy struct before call." + s"Insufficient read permission for field '$field' to copy ${clazz.o + .find[TypeName].map(_.name).getOrElse("class")} before call." override def inlineDescWithSource(source: String): String = s"Insufficient permission for call `$source`." } diff --git a/src/col/vct/col/origin/Origin.scala b/src/col/vct/col/origin/Origin.scala index 987b16e1f4..fd9b9d993d 100644 --- a/src/col/vct/col/origin/Origin.scala +++ b/src/col/vct/col/origin/Origin.scala @@ -109,6 +109,9 @@ case class SourceName(name: String) extends NameStrategy { Some(SourceName.stringToName(name)) } +// Used to disambiguate whether to show a ByValueClass as a class or a struct +case class TypeName(name: String) extends OriginContent + /** Content that provides a bit of context here. By default, this assembles * further context from the remaining origin. contextHere and inlineContextHere * may optionally consume some more contents, otherwise they can just return diff --git a/src/col/vct/col/resolve/Resolve.scala b/src/col/vct/col/resolve/Resolve.scala index 1bd8310820..f88099f286 100644 --- a/src/col/vct/col/resolve/Resolve.scala +++ b/src/col/vct/col/resolve/Resolve.scala @@ -220,7 +220,12 @@ case object ResolveTypes { Spec.findModel(name, ctx) .getOrElse(throw NoSuchNameError("model", name, t)) ) - case t @ TClass(ref, _) => + case t @ TByReferenceClass(ref, _) => + ref.tryResolve(name => + Spec.findClass(name, ctx) + .getOrElse(throw NoSuchNameError("class", name, t)) + ) + case t @ TByValueClass(ref, _) => ref.tryResolve(name => Spec.findClass(name, ctx) .getOrElse(throw NoSuchNameError("class", name, t)) @@ -643,7 +648,7 @@ case object ResolveReferences extends LazyLogging { case endpoint: PVLEndpoint[G] => endpoint.ref = Some( PVL.findConstructor( - TClass(endpoint.cls.decl.ref[Class[G]], Seq()), + TByReferenceClass(endpoint.cls.decl.ref[Class[G]], Seq()), Seq(), endpoint.args, ).getOrElse(throw ConstructorNotFound(endpoint)) diff --git a/src/col/vct/col/resolve/lang/Java.scala b/src/col/vct/col/resolve/lang/Java.scala index b65ced5463..1d734628a7 100644 --- a/src/col/vct/col/resolve/lang/Java.scala +++ b/src/col/vct/col/resolve/lang/Java.scala @@ -42,6 +42,7 @@ import vct.col.ast.{ TArray, TBag, TBool, + TByReferenceClass, TChar, TClass, TEnum, @@ -877,7 +878,7 @@ case object Java extends LazyLogging { case t: TFloat[G] => const(0) case TRational() => const(0) case TZFraction() => const(0) - case TClass(_, _) => Null() + case TByReferenceClass(_, _) => Null() case JavaTClass(_, _) => Null() case TEnum(_) => Null() case TAnyClass() => Null() diff --git a/src/col/vct/col/resolve/lang/PVL.scala b/src/col/vct/col/resolve/lang/PVL.scala index 9a8593e5a4..5de6ae8de2 100644 --- a/src/col/vct/col/resolve/lang/PVL.scala +++ b/src/col/vct/col/resolve/lang/PVL.scala @@ -13,8 +13,8 @@ case object PVL { args: Seq[Expr[G]], ): Option[PVLConstructorTarget[G]] = { t match { - case t @ TClass(Ref(cls), _) => - val resolvedCons = cls.decls.collectFirst { + case t: TClass[G] => + val resolvedCons = t.cls.decl.decls.collectFirst { case cons: PVLConstructor[G] if Util .compat(t.typeEnv, args, typeArgs, cons.args, cons.typeArgs) => @@ -23,7 +23,7 @@ case object PVL { args match { case Nil => - resolvedCons.orElse(Some(ImplicitDefaultPVLConstructor(cls))) + resolvedCons.orElse(Some(ImplicitDefaultPVLConstructor(t.cls.decl))) case _ => resolvedCons } case TModel(Ref(model)) if args.isEmpty => Some(RefModel(model)) @@ -69,7 +69,7 @@ case object PVL { ref.decl.declarations.flatMap(Referrable.from).collectFirst { case ref: RefModelField[G] if ref.name == name => ref } - case TClass(ref, _) => findDerefOfClass(ref.decl, name) + case t: TClass[G] => findDerefOfClass(t.cls.decl, name) case _ => Spec.builtinField(obj, name, blame) } @@ -94,8 +94,8 @@ case object PVL { case ref: RefModelAction[G] if ref.name == method => ref case ref: RefModelProcess[G] if ref.name == method => ref }.orElse(Spec.builtinInstanceMethod(obj, method, blame)) - case t @ TClass(ref, _) => - ref.decl.declarations.flatMap(Referrable.from).collectFirst { + case t: TClass[G] => + t.cls.decl.declarations.flatMap(Referrable.from).collectFirst { case ref: RefInstanceFunction[G] if ref.name == method && Util.compat(t.typeEnv, args, typeArgs, ref.decl) => diff --git a/src/col/vct/col/resolve/lang/Spec.scala b/src/col/vct/col/resolve/lang/Spec.scala index df62509925..52b6cd9a32 100644 --- a/src/col/vct/col/resolve/lang/Spec.scala +++ b/src/col/vct/col/resolve/lang/Spec.scala @@ -348,7 +348,11 @@ case object Spec { def findMethod[G](obj: Expr[G], name: String): Option[InstanceMethod[G]] = obj.t match { - case TClass(Ref(cls), _) => + case TByReferenceClass(Ref(cls), _) => + cls.decls.flatMap(Referrable.from).collectFirst { + case ref @ RefInstanceMethod(decl) if ref.name == name => decl + } + case TByValueClass(Ref(cls), _) => cls.decls.flatMap(Referrable.from).collectFirst { case ref @ RefInstanceMethod(decl) if ref.name == name => decl } @@ -360,7 +364,11 @@ case object Spec { name: String, ): Option[InstanceFunction[G]] = obj.t match { - case TClass(Ref(cls), _) => + case TByReferenceClass(Ref(cls), _) => + cls.decls.flatMap(Referrable.from).collectFirst { + case ref @ RefInstanceFunction(decl) if ref.name == name => decl + } + case TByValueClass(Ref(cls), _) => cls.decls.flatMap(Referrable.from).collectFirst { case ref @ RefInstanceFunction(decl) if ref.name == name => decl } @@ -372,7 +380,11 @@ case object Spec { name: String, ): Option[InstancePredicate[G]] = obj.t match { - case TClass(Ref(cls), _) => + case TByReferenceClass(Ref(cls), _) => + cls.decls.flatMap(Referrable.from).collectFirst { + case ref @ RefInstancePredicate(decl) if ref.name == name => decl + } + case TByValueClass(Ref(cls), _) => cls.decls.flatMap(Referrable.from).collectFirst { case ref @ RefInstancePredicate(decl) if ref.name == name => decl } @@ -385,7 +397,11 @@ case object Spec { def findField[G](obj: Expr[G], name: String): Option[InstanceField[G]] = obj.t match { - case TClass(Ref(cls), _) => + case TByReferenceClass(Ref(cls), _) => + cls.decls.flatMap(Referrable.from).collectFirst { + case ref @ RefField(decl) if ref.name == name => decl + } + case TByValueClass(Ref(cls), _) => cls.decls.flatMap(Referrable.from).collectFirst { case ref @ RefField(decl) if ref.name == name => decl } diff --git a/src/col/vct/col/typerules/CoercingRewriter.scala b/src/col/vct/col/typerules/CoercingRewriter.scala index baf88034cf..26a81d7d8a 100644 --- a/src/col/vct/col/typerules/CoercingRewriter.scala +++ b/src/col/vct/col/typerules/CoercingRewriter.scala @@ -1417,6 +1417,7 @@ abstract class CoercingRewriter[Pre <: Generation]() case LiteralTuple(ts, values) => LiteralTuple(ts, values.zip(ts).map { case (v, t) => coerce(v, t) }) case Local(ref) => Local(ref) + case HeapLocal(ref) => HeapLocal(ref) case LocalThreadId() => LocalThreadId() case MapCons(m, k, v) => val (coercedMap, mapType) = map(m) @@ -2187,7 +2188,6 @@ abstract class CoercingRewriter[Pre <: Generation]() givenMap, yields, ) => - val cls = TClass(ref.decl.cls, classTypeArgs) InvokeConstructor( ref, classTypeArgs, @@ -2237,6 +2237,7 @@ abstract class CoercingRewriter[Pre <: Generation]() case j @ Join(obj) => Join(cls(obj))(j.blame) case Label(decl, stat) => Label(decl, stat) case LocalDecl(local) => LocalDecl(local) + case HeapLocalDecl(local) => HeapLocalDecl(local) case l @ Lock(obj) => Lock(cls(obj))(l.blame) case Loop(init, cond, update, contract, body) => Loop(init, bool(cond), update, contract, body) @@ -2322,13 +2323,14 @@ abstract class CoercingRewriter[Pre <: Generation]() case rule: SimplificationRule[Pre] => new SimplificationRule[Pre](bool(rule.axiom)) case dataType: AxiomaticDataType[Pre] => dataType - case clazz: Class[Pre] => - new Class[Pre]( + case clazz: ByReferenceClass[Pre] => + new ByReferenceClass[Pre]( clazz.typeArgs, clazz.decls, clazz.supports, res(clazz.intrinsicLockInvariant), ) + case clazz: ByValueClass[Pre] => clazz case enum: Enum[Pre] => enum case enumConstant: EnumConstant[Pre] => enumConstant case model: Model[Pre] => model @@ -2447,6 +2449,7 @@ abstract class CoercingRewriter[Pre <: Generation]() case axiom: ADTAxiom[Pre] => new ADTAxiom[Pre](bool(axiom.axiom)) case function: ADTFunction[Pre] => function case variable: Variable[Pre] => variable + case variable: LocalHeapVariable[Pre] => variable case decl: LabelDecl[Pre] => decl case decl: SendDecl[Pre] => decl case decl: ParBlockDecl[Pre] => decl @@ -2587,7 +2590,9 @@ abstract class CoercingRewriter[Pre <: Generation]() // PB: types may very well contain expressions eventually, but for now they don't. def coerce(node: Type[Pre]): Type[Pre] = node match { - case t @ TClass(cls, args) => arity(TClass(cls, args)) + case t @ TByReferenceClass(cls, args) => + arity(TByReferenceClass(cls, args)) + case t @ TByValueClass(cls, args) => arity(TByValueClass(cls, args)) case _ => node } diff --git a/src/col/vct/col/typerules/CoercionUtils.scala b/src/col/vct/col/typerules/CoercionUtils.scala index 47295af0af..deef9b73ca 100644 --- a/src/col/vct/col/typerules/CoercionUtils.scala +++ b/src/col/vct/col/typerules/CoercionUtils.scala @@ -117,7 +117,7 @@ case object CoercionUtils { case (TNull(), TRef()) => CoerceNullRef() case (TNull(), TArray(target)) => CoerceNullArray(target) - case (TNull(), TClass(target, typeArgs)) => + case (TNull(), TByReferenceClass(target, typeArgs)) => CoerceNullClass(target, typeArgs) case (TNull(), JavaTClass(target, _)) => CoerceNullJavaClass(target) case (TNull(), TAnyClass()) => CoerceNullAnyClass() @@ -211,16 +211,15 @@ case object CoercionUtils { CoercionSequence(Seq(CoerceUnboundInt(source, TInt()), CoerceIntRat())) case (_: IntType[G], TRational()) => CoerceIntRat() - case ( - source @ TClass(sourceClass, Seq()), - target @ TClass(targetClass, Seq()), - ) if source.transSupportArrows.exists { case (_, supp) => - supp.cls.decl == targetClass.decl - } => - CoerceSupports(sourceClass, targetClass) + case (source: TClass[G], target: TClass[G]) + if source.typeArgs.isEmpty && target.typeArgs.isEmpty && + source.transSupportArrows().exists { case (_, supp) => + supp.cls.decl == target.cls.decl + } => + CoerceSupports(source.cls, target.cls) - case (source @ TClass(sourceClass, typeArgs), TAnyClass()) => - CoerceClassAnyClass(sourceClass, typeArgs) + case (source: TClass[G], TAnyClass()) => + CoerceClassAnyClass(source.cls, source.typeArgs) case ( source @ JavaTClass(sourceClass, Nil), diff --git a/src/col/vct/col/typerules/Types.scala b/src/col/vct/col/typerules/Types.scala index c06bdb2e45..2aa301264d 100644 --- a/src/col/vct/col/typerules/Types.scala +++ b/src/col/vct/col/typerules/Types.scala @@ -56,7 +56,7 @@ object Types { case (TType(left), TType(right)) => TType(leastCommonSuperType(left, right)) - case (left @ TClass(_, _), right @ TClass(_, _)) => + case (left: TClass[G], right: TClass[G]) => val leftArrows = left.transSupportArrows val rightArrows = right.transSupportArrows // Shared support are classes where there is an incoming left-arrow and right-arrow @@ -79,7 +79,7 @@ object Types { case other => TUnion(other) } - case (TClass(_, _), TAnyClass()) | (TAnyClass(), TClass(_, _)) => + case (_: TClass[G], TAnyClass()) | (TAnyClass(), _: TClass[G]) => TAnyClass() // TODO similar stuff for JavaClass diff --git a/src/col/vct/col/util/AstBuildHelpers.scala b/src/col/vct/col/util/AstBuildHelpers.scala index 0618c8caaa..8cbf2ba839 100644 --- a/src/col/vct/col/util/AstBuildHelpers.scala +++ b/src/col/vct/col/util/AstBuildHelpers.scala @@ -105,6 +105,10 @@ object AstBuildHelpers { SilverLocalAssign(new DirectRef(left), right) } + implicit class LocalHeapVarBuildHelpers[G](left: LocalHeapVariable[G]) { + def get(implicit origin: Origin): HeapLocal[G] = HeapLocal(new DirectRef(left)) + } + implicit class FieldBuildHelpers[G](left: SilverDeref[G]) { def <~(right: Expr[G])( implicit blame: Blame[AssignFailed], @@ -764,6 +768,10 @@ object AstBuildHelpers { implicit o: Origin ): Assign[G] = Assign(local, value)(AssignLocalOk) + def assignHeapLocal[G](local: HeapLocal[G], value: Expr[G])( + implicit o: Origin + ): Assign[G] = Assign(local, value)(AssignLocalOk) + def assignField[G]( obj: Expr[G], field: Ref[G, InstanceField[G]], diff --git a/src/llvm/tools/vcllvm/VCLLVM.cpp b/src/llvm/tools/vcllvm/VCLLVM.cpp index af755ba71f..5cb289dc5b 100644 --- a/src/llvm/tools/vcllvm/VCLLVM.cpp +++ b/src/llvm/tools/vcllvm/VCLLVM.cpp @@ -26,12 +26,12 @@ col::Program sampleCol(bool returnBool) { // class col::GlobalDeclaration *classDeclaration = program.add_declarations(); - col::VctClass *vctClass = classDeclaration->mutable_vct_class(); - llvm2Col::setColNodeId(vctClass); - col::BooleanValue *lockInvariant = vctClass->mutable_intrinsic_lock_invariant()->mutable_boolean_value(); + col::ByReferenceClass *clazz = classDeclaration->mutable_by_reference_class(); + llvm2Col::setColNodeId(clazz); + col::BooleanValue *lockInvariant = clazz->mutable_intrinsic_lock_invariant()->mutable_boolean_value(); lockInvariant->set_value(true); // class>method - col::ClassDeclaration *methodDeclaration = vctClass->add_decls(); + col::ClassDeclaration *methodDeclaration = clazz->add_decls(); col::InstanceMethod *method = methodDeclaration->mutable_instance_method(); llvm2Col::setColNodeId(method); // class>method>return_type diff --git a/src/main/vct/main/stages/Transformation.scala b/src/main/vct/main/stages/Transformation.scala index 36fab123a0..15c61d3b8d 100644 --- a/src/main/vct/main/stages/Transformation.scala +++ b/src/main/vct/main/stages/Transformation.scala @@ -35,6 +35,7 @@ import vct.rewrite.{ HeapVariableToRef, MonomorphizeClass, SmtlibToProverTypes, + EncodeByValueClass, } import vct.rewrite.lang.ReplaceSYCLTypes import vct.rewrite.veymont.{ @@ -325,6 +326,7 @@ case class SilverTransformation( EncodeString, // Encode spec string as seq EncodeChar, CollectLocalDeclarations, // all decls in Scope + EncodeByValueClass, DesugarPermissionOperators, // no PointsTo, \pointer, etc. ReadToValue, // resolve wildcard into fractional permission TrivialAddrOf, diff --git a/src/parsers/vct/parsers/transform/PVLToCol.scala b/src/parsers/vct/parsers/transform/PVLToCol.scala index 5e9d424fa6..9274d0260b 100644 --- a/src/parsers/vct/parsers/transform/PVLToCol.scala +++ b/src/parsers/vct/parsers/transform/PVLToCol.scala @@ -159,7 +159,7 @@ case class PVLToCol[G]( withContract( contract, contract => { - new Class( + new ByReferenceClass( decls = decls.flatMap(convert(_)), supports = Nil, intrinsicLockInvariant = AstBuildHelpers diff --git a/src/parsers/vct/parsers/transform/systemctocol/engine/ClassTransformer.java b/src/parsers/vct/parsers/transform/systemctocol/engine/ClassTransformer.java index b23134a1df..9b44c43783 100644 --- a/src/parsers/vct/parsers/transform/systemctocol/engine/ClassTransformer.java +++ b/src/parsers/vct/parsers/transform/systemctocol/engine/ClassTransformer.java @@ -50,7 +50,7 @@ public Class create_process_class(ProcessClass process) { // Transform class attributes Ref> main_cls_ref = new LazyRef<>(col_system::get_main, Option.empty(), ClassTag$.MODULE$.apply(Class.class)); - InstanceField m = new InstanceField<>(new TClass<>(main_cls_ref, Seqs.empty(), OriGen.create()), col_system.NO_FLAGS, OriGen.create("m")); + InstanceField m = new InstanceField<>(new TByReferenceClass<>(main_cls_ref, Seqs.empty(), OriGen.create()), col_system.NO_FLAGS, OriGen.create("m")); declarations.add(m); col_system.add_class_main_ref(process, m); java.util.Map> fields = create_fields(process.get_generating_function(), process.get_methods(), @@ -75,7 +75,7 @@ public Class create_process_class(ProcessClass process) { // Add all newly generated methods to the declarations as well declarations.addAll(generated_instance_methods); - return new Class<>(Seqs.empty(), + return new ByReferenceClass<>(Seqs.empty(), List.from(CollectionConverters.asScala(declarations)), Seqs.empty(), col_system.TRUE, OriGen.create(create_name(process.get_generating_instance(), process.get_generating_function()))); } @@ -91,7 +91,7 @@ public Class create_state_class(StateClass state_class) { // Transform class attributes Ref> main_cls_ref = new LazyRef<>(col_system::get_main, Option.empty(), ClassTag$.MODULE$.apply(Class.class)); - InstanceField m = new InstanceField<>(new TClass<>(main_cls_ref, Seqs.empty(), OriGen.create()), col_system.NO_FLAGS, OriGen.create("m")); + InstanceField m = new InstanceField<>(new TByReferenceClass<>(main_cls_ref, Seqs.empty(), OriGen.create()), col_system.NO_FLAGS, OriGen.create("m")); declarations.add(m); col_system.add_class_main_ref(state_class, m); java.util.Map> fields = create_fields(null, state_class.get_methods(), @@ -126,7 +126,7 @@ public Class create_state_class(StateClass state_class) { // Add newly generated methods to declaration list declarations.addAll(generated_instance_methods); - return new Class<>(Seqs.empty(), + return new ByReferenceClass<>(Seqs.empty(), List.from(CollectionConverters.asScala(declarations)), Seqs.empty(), col_system.TRUE, OriGen.create(create_name(state_class.get_generating_instance()))); } diff --git a/src/parsers/vct/parsers/transform/systemctocol/engine/KnownTypeTransformer.java b/src/parsers/vct/parsers/transform/systemctocol/engine/KnownTypeTransformer.java index 3165bba55d..5cc5087f12 100644 --- a/src/parsers/vct/parsers/transform/systemctocol/engine/KnownTypeTransformer.java +++ b/src/parsers/vct/parsers/transform/systemctocol/engine/KnownTypeTransformer.java @@ -92,7 +92,7 @@ public void transform() { // Add channel field to COL system Ref> ref_to_cls = new DirectRef<>(cls, ClassTag$.MODULE$.apply(Class.class)); - col_system.add_primitive_channel(sc_inst, new InstanceField<>(new TClass<>(ref_to_cls, Seqs.empty(), OriGen.create()), col_system.NO_FLAGS, + col_system.add_primitive_channel(sc_inst, new InstanceField<>(new TByReferenceClass<>(ref_to_cls, Seqs.empty(), OriGen.create()), col_system.NO_FLAGS, OriGen.create(name.toLowerCase()))); } @@ -119,7 +119,7 @@ private String generate_class_name() { private Class transform_fifo(Origin o, Type t) { // Class fields Ref> main_cls_ref = new LazyRef<>(col_system::get_main, Option.empty(), ClassTag$.MODULE$.apply(Class.class)); - InstanceField m = new InstanceField<>(new TClass<>(main_cls_ref, Seqs.empty(), OriGen.create()), col_system.NO_FLAGS, OriGen.create("m")); + InstanceField m = new InstanceField<>(new TByReferenceClass<>(main_cls_ref, Seqs.empty(), OriGen.create()), col_system.NO_FLAGS, OriGen.create("m")); InstanceField buf = new InstanceField<>(new TSeq<>(t, OriGen.create()), col_system.NO_FLAGS, OriGen.create("buffer")); InstanceField nr_read = new InstanceField<>(col_system.T_INT, col_system.NO_FLAGS, OriGen.create("num_read")); InstanceField written = new InstanceField<>(new TSeq<>(t, OriGen.create()), col_system.NO_FLAGS, OriGen.create("written")); @@ -144,7 +144,7 @@ private Class transform_fifo(Origin o, Type t) { // Create the class java.util.List> declarations = java.util.List.of(m, buf, nr_read, written, constructor, fifo_read, fifo_write, fifo_update); - return new Class<>(Seqs.empty(), List.from(CollectionConverters.asScala(declarations)), Seqs.empty(), col_system.TRUE, o); + return new ByReferenceClass<>(Seqs.empty(), List.from(CollectionConverters.asScala(declarations)), Seqs.empty(), col_system.TRUE, o); } /** @@ -524,7 +524,7 @@ private InstanceMethod create_fifo_update_method(InstanceField m, Instance private Class transform_signal(Origin o, Type t) { // Class fields Ref> main_cls_ref = new LazyRef<>(col_system::get_main, Option.empty(), ClassTag$.MODULE$.apply(Class.class)); - InstanceField m = new InstanceField<>(new TClass<>(main_cls_ref, Seqs.empty(), OriGen.create()), col_system.NO_FLAGS, OriGen.create("m")); + InstanceField m = new InstanceField<>(new TByReferenceClass<>(main_cls_ref, Seqs.empty(), OriGen.create()), col_system.NO_FLAGS, OriGen.create("m")); InstanceField val = new InstanceField<>(t, col_system.NO_FLAGS, OriGen.create("val")); InstanceField _val = new InstanceField<>(t, col_system.NO_FLAGS, OriGen.create("_val")); @@ -545,7 +545,7 @@ private Class transform_signal(Origin o, Type t) { // Create the class java.util.List> class_content = java.util.List.of(m, val, _val, constructor, signal_read, signal_write, signal_update); - return new Class<>(Seqs.empty(), + return new ByReferenceClass<>(Seqs.empty(), List.from(CollectionConverters.asScala(class_content)), Seqs.empty(), col_system.TRUE, o); } diff --git a/src/parsers/vct/parsers/transform/systemctocol/engine/MainTransformer.java b/src/parsers/vct/parsers/transform/systemctocol/engine/MainTransformer.java index b9ff6d8305..01a1850e43 100644 --- a/src/parsers/vct/parsers/transform/systemctocol/engine/MainTransformer.java +++ b/src/parsers/vct/parsers/transform/systemctocol/engine/MainTransformer.java @@ -189,7 +189,7 @@ private void create_instances() { // Get field type Class transformed_class = col_system.get_col_class_translation(process_class); Ref> ref_to_class = new DirectRef<>(transformed_class, ClassTag$.MODULE$.apply(Class.class)); - Type t = new TClass<>(ref_to_class, Seqs.empty(), OriGen.create()); + Type t = new TByReferenceClass<>(ref_to_class, Seqs.empty(), OriGen.create()); // Generate instance field InstanceField inst = new InstanceField<>(t, col_system.NO_FLAGS, OriGen.create(create_instance_name(process_class))); @@ -204,7 +204,7 @@ private void create_instances() { // Get field type Class transformed_class = col_system.get_col_class_translation(state_class); Ref> ref_to_class = new DirectRef<>(transformed_class, ClassTag$.MODULE$.apply(Class.class)); - Type t = new TClass<>(ref_to_class, Seqs.empty(), OriGen.create()); + Type t = new TByReferenceClass<>(ref_to_class, Seqs.empty(), OriGen.create()); // Generate instance field InstanceField inst = new InstanceField<>(t, col_system.NO_FLAGS, OriGen.create(create_instance_name(state_class))); @@ -1237,7 +1237,7 @@ private void assemble_main() { new WritePerm<>(OriGen.create()), OriGen.create()); // Assemble class - Class main_class = new Class<>(Seqs.empty(), List.from(CollectionConverters.asScala(declarations)), + Class main_class = new ByReferenceClass<>(Seqs.empty(), List.from(CollectionConverters.asScala(declarations)), Seqs.empty(), lock_invariant, OriGen.create("Main")); // Register Main class in COL system context diff --git a/src/rewrite/vct/rewrite/CheckContractSatisfiability.scala b/src/rewrite/vct/rewrite/CheckContractSatisfiability.scala index 04f5e18c17..0f8b9ffe4a 100644 --- a/src/rewrite/vct/rewrite/CheckContractSatisfiability.scala +++ b/src/rewrite/vct/rewrite/CheckContractSatisfiability.scala @@ -97,23 +97,25 @@ case class CheckContractSatisfiability[Pre <: Generation]( val result = extractObj.extract(pred) val extractObj.Data(ts, in, _, _, _) = extractObj.finish() variables.scope { - globalDeclarations.declare(procedure( - blame = PanicBlame( - "The postcondition of a method checking satisfiability is empty" - ), - contractBlame = UnsafeDontCare.Satisfiability( - "the precondition of a check-sat method is only there to check it." - ), - requires = - UnitAccountedPredicate( - wellFormednessBlame.having(NotWellFormedIgnoreCheckSat(err)) { - dispatch(result) - } - )(result.o), - typeArgs = variables.dispatch(ts.keys), - args = variables.dispatch(in.keys), - body = Some(Scope[Post](Nil, Assert(ff)(onlyAssertBlame))), - )) + localHeapVariables.scope { + globalDeclarations.declare(procedure( + blame = PanicBlame( + "The postcondition of a method checking satisfiability is empty" + ), + contractBlame = UnsafeDontCare.Satisfiability( + "the precondition of a check-sat method is only there to check it." + ), + requires = + UnitAccountedPredicate( + wellFormednessBlame.having(NotWellFormedIgnoreCheckSat(err)) { + dispatch(result) + } + )(result.o), + typeArgs = variables.dispatch(ts.keys), + args = variables.dispatch(in.keys), + body = Some(Scope[Post](Nil, Assert(ff)(onlyAssertBlame))), + )) + } } } } diff --git a/src/rewrite/vct/rewrite/CheckProcessAlgebra.scala b/src/rewrite/vct/rewrite/CheckProcessAlgebra.scala index 87f378717b..a38730d7be 100644 --- a/src/rewrite/vct/rewrite/CheckProcessAlgebra.scala +++ b/src/rewrite/vct/rewrite/CheckProcessAlgebra.scala @@ -95,7 +95,7 @@ case class CheckProcessAlgebra[Pre <: Generation]() val newClass = currentModel.having(model) { - new Class( + new ByReferenceClass( Seq(), classDeclarations.collect { model.declarations.foreach(dispatch(_)) diff --git a/src/rewrite/vct/rewrite/ClassToRef.scala b/src/rewrite/vct/rewrite/ClassToRef.scala index 32cfe554f9..b73c52606a 100644 --- a/src/rewrite/vct/rewrite/ClassToRef.scala +++ b/src/rewrite/vct/rewrite/ClassToRef.scala @@ -2,13 +2,12 @@ package vct.col.rewrite import vct.col.ast._ import vct.col.origin._ +import vct.result.VerificationError import vct.col.util.AstBuildHelpers._ import hre.util.ScopedStack import vct.col.rewrite.error.{ExcludedByPassOrder, ExtraNode} import vct.col.ref.Ref -import vct.col.rewrite.{Generation, Rewriter, RewriterBuilder, Rewritten} import vct.col.util.SuccessionMap -import RewriteHelpers._ import scala.collection.mutable @@ -30,6 +29,7 @@ case object ClassToRef extends RewriterBuilder { override def blame(error: PreconditionFailed): Unit = inner.blame(InstanceNull(inv)) } + } case class ClassToRef[Pre <: Generation]() extends Rewriter[Pre] { @@ -75,6 +75,27 @@ case class ClassToRef[Pre <: Generation]() extends Rewriter[Pre] { ) } + def transitiveByValuePermissions( + obj: Expr[Pre], + t: TByValueClass[Pre], + amount: Expr[Pre], + )(implicit o: Origin): Expr[Pre] = { + t.cls.decl.decls.collect[Expr[Pre]] { case field: InstanceField[Pre] => + field.t match { + case field_t: TByValueClass[Pre] => + fieldPerm[Pre](obj, field.ref, amount) &* + transitiveByValuePermissions( + Deref[Pre](obj, field.ref)(PanicBlame( + "Permission should already be ensured" + )), + field_t, + amount, + ) + case _ => fieldPerm(obj, field.ref, amount) + } + }.reduce[Expr[Pre]] { (a, b) => a &* b } + } + def makeInstanceOf: Function[Post] = { implicit val o: Origin = InstanceOfOrigin val sub = new Variable[Post](TInt()) @@ -117,7 +138,7 @@ case class ClassToRef[Pre <: Generation]() extends Rewriter[Pre] { decl match { case cls: Class[Pre] => if (cls.typeArgs.nonEmpty) - throw vct.result.VerificationError.Unreachable( + throw VerificationError.Unreachable( "Class type parameters should be encoded using monomorphization earlier" ) @@ -407,7 +428,8 @@ case class ClassToRef[Pre <: Generation]() extends Rewriter[Pre] { ) case TypeValue(t) => t match { - case TClass(Ref(cls), Seq()) => const(typeNumber(cls))(e.o) + case t: TClass[Pre] if t.typeArgs.isEmpty => + const(typeNumber(t.cls.decl))(e.o) case other => ??? } case TypeOf(value) => @@ -471,7 +493,7 @@ case class ClassToRef[Pre <: Generation]() extends Rewriter[Pre] { override def dispatch(t: Type[Pre]): Type[Post] = t match { - case TClass(_, _) => TRef() + case _: TClass[Pre] => TRef() case TAnyClass() => TRef() case t => rewriteDefault(t) } diff --git a/src/rewrite/vct/rewrite/ConstantifyFinalFields.scala b/src/rewrite/vct/rewrite/ConstantifyFinalFields.scala index 67ab5cceb6..3594585c57 100644 --- a/src/rewrite/vct/rewrite/ConstantifyFinalFields.scala +++ b/src/rewrite/vct/rewrite/ConstantifyFinalFields.scala @@ -80,12 +80,22 @@ case class ConstantifyFinalFields[Pre <: Generation]() extends Rewriter[Pre] { implicit val o: Origin = field.o if (isFinal(field)) { val `this` = - new Variable[Post](TClass( - succ(currentClass.top), - currentClass.top.typeArgs.map { v: Variable[Pre] => - TVar(succ(v)) - }, - )) + currentClass.top match { + case _: ByReferenceClass[Pre] => + new Variable[Post](TByReferenceClass( + succ(currentClass.top), + currentClass.top.typeArgs.map { v: Variable[Pre] => + TVar(succ(v)) + }, + )) + case _: ByValueClass[Pre] => + new Variable[Post](TByValueClass( + succ(currentClass.top), + currentClass.top.typeArgs.map { v: Variable[Pre] => + TVar(succ(v)) + }, + )) + } fieldFunction(field) = globalDeclarations .declare(withResult((result: Result[Post]) => function[Post]( diff --git a/src/rewrite/vct/rewrite/EncodeArrayValues.scala b/src/rewrite/vct/rewrite/EncodeArrayValues.scala index dcb8e47ab8..7c1732761f 100644 --- a/src/rewrite/vct/rewrite/EncodeArrayValues.scala +++ b/src/rewrite/vct/rewrite/EncodeArrayValues.scala @@ -423,8 +423,8 @@ case class EncodeArrayValues[Pre <: Generation]() extends Rewriter[Pre] { val fields = structType match { - case TClass(ref, _) => - ref.decl.declarations.collect { case field: InstanceField[Post] => + case t: TClass[Post] => + t.cls.decl.declarations.collect { case field: InstanceField[Post] => field } case _ => Seq() diff --git a/src/rewrite/vct/rewrite/EncodeAutoValue.scala b/src/rewrite/vct/rewrite/EncodeAutoValue.scala index 0e2beaf27f..c9c71db2e5 100644 --- a/src/rewrite/vct/rewrite/EncodeAutoValue.scala +++ b/src/rewrite/vct/rewrite/EncodeAutoValue.scala @@ -179,31 +179,33 @@ case class EncodeAutoValue[Pre <: Generation]() extends Rewriter[Pre] { } case Let(binding, value, main) => variables.scope { - val top = conditionContext.pop() - val (b, v) = - try { (variables.dispatch(binding), dispatch(value)) } - finally { conditionContext.push(top) } - val mMap = mutable.ArrayBuffer[(Expr[Pre], Expr[Post])]() - val m = - conditionContext.having((conditionContext.top._1, mMap)) { - dispatch(main) - } - if (mMap.isEmpty) { Let(b, v, m) } - else { - mMap.foreach(postM => - conditionContext.top._2 - .append((Let(binding, value, postM._1), Let(b, v, postM._2))) - ) - conditionContext.top._1 match { - case InPrecondition() => Let(b, v, m) - case InPostcondition() => - Let( - b, - Old(v, None)(PanicBlame( - "Old should always be valid in a postcondition" - )), - m, - ) + localHeapVariables.scope { + val top = conditionContext.pop() + val (b, v) = + try { (variables.dispatch(binding), dispatch(value)) } + finally { conditionContext.push(top) } + val mMap = mutable.ArrayBuffer[(Expr[Pre], Expr[Post])]() + val m = + conditionContext.having((conditionContext.top._1, mMap)) { + dispatch(main) + } + if (mMap.isEmpty) { Let(b, v, m) } + else { + mMap.foreach(postM => + conditionContext.top._2 + .append((Let(binding, value, postM._1), Let(b, v, postM._2))) + ) + conditionContext.top._1 match { + case InPrecondition() => Let(b, v, m) + case InPostcondition() => + Let( + b, + Old(v, None)(PanicBlame( + "Old should always be valid in a postcondition" + )), + m, + ) + } } } } diff --git a/src/rewrite/vct/rewrite/EncodeByValueClass.scala b/src/rewrite/vct/rewrite/EncodeByValueClass.scala new file mode 100644 index 0000000000..3054e2572c --- /dev/null +++ b/src/rewrite/vct/rewrite/EncodeByValueClass.scala @@ -0,0 +1,249 @@ +package vct.rewrite + +import hre.util.ScopedStack +import vct.col.ast._ +import vct.col.origin._ +import vct.col.ref.Ref +import vct.col.resolve.ctx.Referrable +import vct.col.rewrite.{Generation, Rewriter, RewriterBuilder} +import vct.col.util.AstBuildHelpers._ +import vct.result.VerificationError.UserError + +case object EncodeByValueClass extends RewriterBuilder { + override def key: String = "encodeByValueClass" + + override def desc: String = + "Initialise ByValueClasses when they are declared and copy them whenever they're read" + + private case class ClassCopyInAssignmentFailed( + blame: Blame[AssignFailed], + assign: Node[_], + clazz: ByValueClass[_], + field: InstanceField[_], + ) extends Blame[InsufficientPermission] { + override def blame(error: InsufficientPermission): Unit = { + if (blame.isInstanceOf[PanicBlame]) { + assign.o + .blame(CopyClassFailed(assign, clazz, Referrable.originName(field))) + } else { + blame + .blame(CopyClassFailed(assign, clazz, Referrable.originName(field))) + } + } + } + + private case class ClassCopyInCallFailed( + blame: Blame[InvocationFailure], + inv: Invocation[_], + clazz: ByValueClass[_], + field: InstanceField[_], + ) extends Blame[InsufficientPermission] { + override def blame(error: InsufficientPermission): Unit = { + blame.blame( + CopyClassFailedBeforeCall(inv, clazz, Referrable.originName(field)) + ) + } + } + + case class UnsupportedStructPerm(o: Origin) extends UserError { + override def code: String = "unsupportedStructPerm" + override def text: String = + o.messageInContext( + "Shorthand for Permissions for structs not possible, since the struct has a cyclic reference" + ) + } + + private sealed class CopyContext + + private case class InCall(invocation: Invocation[_]) extends CopyContext + + private case class InAssignmentExpression(assignment: AssignExpression[_]) + extends CopyContext + + private case class InAssignmentStatement(assignment: Assign[_]) + extends CopyContext +} + +case class EncodeByValueClass[Pre <: Generation]() extends Rewriter[Pre] { + + import EncodeByValueClass._ + + private val inAssignment: ScopedStack[Unit] = ScopedStack() + private val copyContext: ScopedStack[CopyContext] = ScopedStack() + + override def dispatch(node: Statement[Pre]): Statement[Post] = + node match { + case s: Scope[Pre] => + cPPLocalDeclarations.scope { + cLocalDeclarations.scope { + variables.scope { + localHeapVariables.scope { + val locals = variables.dispatch(s.locals) + Scope( + locals, + Block(locals.collect { + case v: Variable[Post] + if v.t.isInstanceOf[TByValueClass[Post]] => + Assign( + v.get(v.o), + NewObject(v.t.asInstanceOf[TByValueClass[Post]].cls)(v.o), + )(PanicBlame( + "Instantiating a ByValueClass should always succeed" + ))(v.o) + } ++ Seq(s.body.rewriteDefault()))(node.o), + )(node.o) + } + } + } + } + case assign: Assign[Pre] => { + val target = inAssignment.having(()) { assign.target.rewriteDefault() } + copyContext.having(InAssignmentStatement(assign)) { + assign.rewrite(target = target) + } + } + case _ => node.rewriteDefault() + } + + private def copyClassValue( + obj: Expr[Post], + t: TByValueClass[Pre], + blame: InstanceField[Pre] => Blame[InsufficientPermission], + ): Expr[Post] = { + implicit val o: Origin = obj.o + val v = new Variable[Post](dispatch(t)) + val children = t.cls.decl.decls.collect { case f: InstanceField[Pre] => + f.t match { + case inner: TByValueClass[Pre] => + Assign[Post]( + Deref[Post](v.get, succ(f))(DerefAssignTarget), + copyClassValue(Deref[Post](obj, succ(f))(blame(f)), inner, blame), + )(AssignLocalOk) + case _ => + Assign[Post]( + Deref[Post](v.get, succ(f))(DerefAssignTarget), + Deref[Post](obj, succ(f))(blame(f)), + )(AssignLocalOk) + + } + } + ScopedExpr( + Seq(v), + Then( + PreAssignExpression(v.get, NewObject[Post](succ(t.cls.decl)))( + AssignLocalOk + ), + Block(children), + ), + ) + } + + // def unwrapClassPerm( + // struct: Expr[Post], + // perm: Expr[Pre], + // structType: TByValueClass[Pre], + // origin: Origin, + // visited: Seq[TByValueClass[Pre]] = Seq(), + // ): Expr[Post] = { + // if (visited.contains(structType)) + // throw UnsupportedStructPerm( + // origin + // ) // We do not allow this notation for recursive structs + // implicit val o: Origin = origin + // val blame = PanicBlame("Field permission is framed") + // val Seq(CStructDeclaration(_, fields)) = structType.ref.decl.decl.specs + // val newPerm = dispatch(perm) + // val AmbiguousLocation(newExpr) = struct + // val newFieldPerms = fields.map(member => { + // val loc = + // AmbiguousLocation( + // Deref[Post]( + // newExpr, + // cStructFieldsSuccessor.ref((structType.ref.decl, member)), + // )(blame) + // )(struct.blame) + // member.specs.collectFirst { + // case CSpecificationType(newStruct: CTStruct[Pre]) => + // // We recurse, since a field is another struct + // Perm(loc, newPerm) &* unwrapStructPerm( + // loc, + // perm, + // newStruct, + // origin, + // structType +: visited, + // ) + // }.getOrElse(Perm(loc, newPerm)) + // }) + + // foldStar(newFieldPerms) + // } + override def dispatch(node: Expr[Pre]): Expr[Post] = + if (inAssignment.nonEmpty) + node.rewriteDefault() + else + node match { + case Perm(loc, p) => node.rewriteDefault() + case assign: PreAssignExpression[Pre] => + val target = + inAssignment.having(()) { assign.target.rewriteDefault() } + copyContext.having(InAssignmentExpression(assign)) { + assign.rewrite(target = target) + } + case invocation: Invocation[Pre] => { + copyContext.having(InCall(invocation)) { invocation.rewriteDefault() } + } + case Local(Ref(v)) if v.t.isInstanceOf[TByValueClass[Pre]] => + if (copyContext.isEmpty) { + return node.rewriteDefault() + } // If we are in other kinds of expressions like if statements + val t = v.t.asInstanceOf[TByValueClass[Pre]] + val clazz = t.cls.decl.asInstanceOf[ByValueClass[Pre]] + + copyContext.top match { + case InCall(invocation) => + copyClassValue( + node.rewriteDefault(), + t, + f => + ClassCopyInCallFailed(invocation.blame, invocation, clazz, f), + ) + case InAssignmentExpression(assignment: PreAssignExpression[_]) => + copyClassValue( + node.rewriteDefault(), + t, + f => + ClassCopyInAssignmentFailed( + assignment.blame, + assignment, + clazz, + f, + ), + ) + case InAssignmentExpression(assignment: PostAssignExpression[_]) => + copyClassValue( + node.rewriteDefault(), + t, + f => + ClassCopyInAssignmentFailed( + assignment.blame, + assignment, + clazz, + f, + ), + ) + case InAssignmentStatement(assignment) => + copyClassValue( + node.rewriteDefault(), + t, + f => + ClassCopyInAssignmentFailed( + assignment.blame, + assignment, + clazz, + f, + ), + ) + } + case _ => node.rewriteDefault() + } +} diff --git a/src/rewrite/vct/rewrite/EncodeForkJoin.scala b/src/rewrite/vct/rewrite/EncodeForkJoin.scala index 961b4e8b2e..49a762d685 100644 --- a/src/rewrite/vct/rewrite/EncodeForkJoin.scala +++ b/src/rewrite/vct/rewrite/EncodeForkJoin.scala @@ -129,7 +129,13 @@ case class EncodeForkJoin[Pre <: Generation]() extends Rewriter[Pre] { implicit val o: Origin = e.o cls.decls.collectFirst { case run: RunMethod[Pre] => run } match { case Some(_) => - val obj = new Variable[Post](TClass(succ(cls), Seq())) + val obj = + cls match { + case _: ByReferenceClass[Pre] => + new Variable[Post](TByReferenceClass(succ(cls), Seq())) + case _: ByValueClass[Pre] => + new Variable[Post](TByValueClass(succ(cls), Seq())) + } ScopedExpr( Seq(obj), With( diff --git a/src/rewrite/vct/rewrite/EncodeIntrinsicLock.scala b/src/rewrite/vct/rewrite/EncodeIntrinsicLock.scala index c5308b7b26..def6ba5911 100644 --- a/src/rewrite/vct/rewrite/EncodeIntrinsicLock.scala +++ b/src/rewrite/vct/rewrite/EncodeIntrinsicLock.scala @@ -85,7 +85,7 @@ case class EncodeIntrinsicLock[Pre <: Generation]() extends Rewriter[Pre] { def getClass(obj: Expr[Pre]): Class[Pre] = obj.t match { - case TClass(Ref(cls), _) => cls + case t: TClass[Pre] => t.cls.decl case _ => throw UnreachableAfterTypeCheck( "This argument is not a class type.", @@ -153,7 +153,7 @@ case class EncodeIntrinsicLock[Pre <: Generation]() extends Rewriter[Pre] { override def dispatch(decl: Declaration[Pre]): Unit = decl match { - case cls: Class[Pre] => + case cls: ByReferenceClass[Pre] => globalDeclarations.succeed( cls, cls.rewrite( diff --git a/src/rewrite/vct/rewrite/EncodeResourceValues.scala b/src/rewrite/vct/rewrite/EncodeResourceValues.scala index 531858084e..0c99f1a948 100644 --- a/src/rewrite/vct/rewrite/EncodeResourceValues.scala +++ b/src/rewrite/vct/rewrite/EncodeResourceValues.scala @@ -188,7 +188,11 @@ case class EncodeResourceValues[Pre <: Generation]() case ResourcePattern.HeapVariableLocation(_) => Nil case ResourcePattern.FieldLocation(f) => nonGeneric(fieldOwner(f)) - Seq(TClass(succ(fieldOwner(f)), Seq())) + Seq(fieldOwner(f) match { + case cls: ByReferenceClass[Pre] => + TByReferenceClass(succ(cls), Seq()) + case cls: ByValueClass[Pre] => TByValueClass(succ(cls), Seq()) + }) case ResourcePattern.ModelLocation(f) => Seq(TModel(succ(modelFieldOwner(f)))) case ResourcePattern.SilverFieldLocation(_) => Seq(TRef()) @@ -200,8 +204,12 @@ case class EncodeResourceValues[Pre <: Generation]() ref.args.map(_.t).map(dispatch) case ResourcePattern.InstancePredicateLocation(ref) => nonGeneric(predicateOwner(ref)) - TClass[Post](succ(predicateOwner(ref)), Seq()) +: - ref.args.map(_.t).map(dispatch) + (predicateOwner(ref) match { + case cls: ByReferenceClass[Pre] => + TByReferenceClass(succ[Class[Post]](cls), Seq()) + case cls: ByValueClass[Pre] => + TByValueClass(succ[Class[Post]](cls), Seq()) + }) +: ref.args.map(_.t).map(dispatch) } def freeTypes(pattern: ResourcePattern): Seq[Type[Post]] = @@ -212,8 +220,12 @@ case class EncodeResourceValues[Pre <: Generation]() case ResourcePattern.Predicate(p) => p.args.map(_.t).map(dispatch) case ResourcePattern.InstancePredicate(p) => nonGeneric(predicateOwner(p)) - TClass[Post](succ(predicateOwner(p)), Seq()) +: p.args.map(_.t) - .map(dispatch) + (predicateOwner(p) match { + case cls: ByReferenceClass[Pre] => + TByReferenceClass(succ[Class[Post]](cls), Seq()) + case cls: ByValueClass[Pre] => + TByValueClass(succ[Class[Post]](cls), Seq()) + }) +: p.args.map(_.t).map(dispatch) case ResourcePattern.Star(left, right) => freeTypes(left) ++ freeTypes(right) case ResourcePattern.Implies(res) => freeTypes(res) diff --git a/src/rewrite/vct/rewrite/ExtractInlineQuantifierPatterns.scala b/src/rewrite/vct/rewrite/ExtractInlineQuantifierPatterns.scala index 825d188c08..2768359d6e 100644 --- a/src/rewrite/vct/rewrite/ExtractInlineQuantifierPatterns.scala +++ b/src/rewrite/vct/rewrite/ExtractInlineQuantifierPatterns.scala @@ -111,50 +111,56 @@ case class ExtractInlineQuantifierPatterns[Pre <: Generation]() case f: Forall[Pre] => variables.scope { - val (patternsHere, body) = patterns.collect { - // We only want to inline lets that are defined inside the quantifier - letBindings.having(ScopedStack()) { dispatch(f.body) } + localHeapVariables.scope { + val (patternsHere, body) = patterns.collect { + // We only want to inline lets that are defined inside the quantifier + letBindings.having(ScopedStack()) { dispatch(f.body) } + } + val unsortedGroups = patternsHere.groupBy(_.group) + val sortedGroups = unsortedGroups.toSeq.sortBy(_._1).map(_._2) + val triggers = sortedGroups.map(_.map(_.make())) + Forall( + bindings = variables.collect { f.bindings.foreach(dispatch) }._1, + triggers = f.triggers.map(_.map(dispatch)) ++ triggers, + body = body, + )(f.o) } - val unsortedGroups = patternsHere.groupBy(_.group) - val sortedGroups = unsortedGroups.toSeq.sortBy(_._1).map(_._2) - val triggers = sortedGroups.map(_.map(_.make())) - Forall( - bindings = variables.collect { f.bindings.foreach(dispatch) }._1, - triggers = f.triggers.map(_.map(dispatch)) ++ triggers, - body = body, - )(f.o) } case f: Starall[Pre] => variables.scope { - val (patternsHere, body) = patterns.collect { - // We only want to inline lets that are defined inside the quantifier - letBindings.having(ScopedStack()) { dispatch(f.body) } + localHeapVariables.scope { + val (patternsHere, body) = patterns.collect { + // We only want to inline lets that are defined inside the quantifier + letBindings.having(ScopedStack()) { dispatch(f.body) } + } + val unsortedGroups = patternsHere.groupBy(_.group) + val sortedGroups = unsortedGroups.toSeq.sortBy(_._1).map(_._2) + val triggers = sortedGroups.map(_.map(_.make())) + Starall( + bindings = variables.collect { f.bindings.foreach(dispatch) }._1, + triggers = f.triggers.map(_.map(dispatch)) ++ triggers, + body = body, + )(f.blame)(f.o) } - val unsortedGroups = patternsHere.groupBy(_.group) - val sortedGroups = unsortedGroups.toSeq.sortBy(_._1).map(_._2) - val triggers = sortedGroups.map(_.map(_.make())) - Starall( - bindings = variables.collect { f.bindings.foreach(dispatch) }._1, - triggers = f.triggers.map(_.map(dispatch)) ++ triggers, - body = body, - )(f.blame)(f.o) } case f: Exists[Pre] => variables.scope { - val (patternsHere, body) = patterns.collect { - // We only want to inline lets that are defined inside the quantifier - letBindings.having(ScopedStack()) { dispatch(f.body) } + localHeapVariables.scope { + val (patternsHere, body) = patterns.collect { + // We only want to inline lets that are defined inside the quantifier + letBindings.having(ScopedStack()) { dispatch(f.body) } + } + val unsortedGroups = patternsHere.groupBy(_.group) + val sortedGroups = unsortedGroups.toSeq.sortBy(_._1).map(_._2) + val triggers = sortedGroups.map(_.map(_.make())) + Exists( + bindings = variables.collect { f.bindings.foreach(dispatch) }._1, + triggers = f.triggers.map(_.map(dispatch)) ++ triggers, + body = body, + )(f.o) } - val unsortedGroups = patternsHere.groupBy(_.group) - val sortedGroups = unsortedGroups.toSeq.sortBy(_._1).map(_._2) - val triggers = sortedGroups.map(_.map(_.make())) - Exists( - bindings = variables.collect { f.bindings.foreach(dispatch) }._1, - triggers = f.triggers.map(_.map(dispatch)) ++ triggers, - body = body, - )(f.o) } case other => rewriteDefault(other) diff --git a/src/rewrite/vct/rewrite/MonomorphizeClass.scala b/src/rewrite/vct/rewrite/MonomorphizeClass.scala index 779fa700e4..dd91b27be4 100644 --- a/src/rewrite/vct/rewrite/MonomorphizeClass.scala +++ b/src/rewrite/vct/rewrite/MonomorphizeClass.scala @@ -82,9 +82,16 @@ case class MonomorphizeClass[Pre <: Generation]() globalDeclarations.scope { classDeclarations.scope { variables.scope { - allScopes.anyDeclare( - allScopes.anySucceedOnly(cls, cls.rewrite(typeArgs = Seq())) - ) + localHeapVariables.scope { + allScopes.anyDeclare(allScopes.anySucceedOnly( + cls, + cls match { + case cls: ByReferenceClass[Pre] => + cls.rewrite(typeArgs = Seq()) + case cls: ByValueClass[Pre] => cls.rewrite(typeArgs = Seq()) + }, + )) + } } } } @@ -130,14 +137,25 @@ case class MonomorphizeClass[Pre <: Generation]() override def dispatch(t: Type[Pre]): Type[Post] = (t, ctx.topOption) match { - case (TClass(Ref(cls), typeArgs), ctx) if typeArgs.nonEmpty => + case (TByReferenceClass(Ref(cls), typeArgs), ctx) if typeArgs.nonEmpty => + val typeValues = + ctx match { + case Some(ctx) => typeArgs.map(ctx.substitute.dispatch) + case None => typeArgs + } + instantiate(cls, typeValues, false) + TByReferenceClass[Post]( + genericSucc.ref[Post, Class[Post]](((cls, typeValues), cls)), + Seq(), + ) + case (TByValueClass(Ref(cls), typeArgs), ctx) if typeArgs.nonEmpty => val typeValues = ctx match { case Some(ctx) => typeArgs.map(ctx.substitute.dispatch) case None => typeArgs } instantiate(cls, typeValues, false) - TClass[Post]( + TByValueClass[Post]( genericSucc.ref[Post, Class[Post]](((cls, typeValues), cls)), Seq(), ) @@ -158,13 +176,13 @@ case class MonomorphizeClass[Pre <: Generation]() ) case inv: InvokeMethod[Pre] => inv.obj.t match { - case TClass(Ref(cls), typeArgs) if typeArgs.nonEmpty => - val typeValues = ctx.topOption.map(_.evalTypes(typeArgs)) - .getOrElse(typeArgs) - instantiate(cls, typeValues, false) + case t: TClass[Pre] if t.typeArgs.nonEmpty => + val typeValues = ctx.topOption.map(_.evalTypes(t.typeArgs)) + .getOrElse(t.typeArgs) + instantiate(t.cls.decl, typeValues, false) inv.rewrite(ref = genericSucc.ref[Post, InstanceMethod[Post]]( - ((cls, typeValues), inv.ref.decl) + ((t.cls.decl, typeValues), inv.ref.decl) ) ) case _ => inv.rewriteDefault() @@ -176,13 +194,14 @@ case class MonomorphizeClass[Pre <: Generation]() loc match { case loc @ FieldLocation(obj, Ref(field)) => obj.t match { - case TClass(Ref(cls), typeArgs) if typeArgs.nonEmpty => - val typeArgs1 = ctx.topOption.map(_.evalTypes(typeArgs)) - .getOrElse(typeArgs) - instantiate(cls, typeArgs1, false) + case t: TClass[Pre] if t.typeArgs.nonEmpty => + val typeArgs1 = ctx.topOption.map(_.evalTypes(t.typeArgs)) + .getOrElse(t.typeArgs) + instantiate(t.cls.decl, typeArgs1, false) loc.rewrite(field = - genericSucc - .ref[Post, InstanceField[Post]](((cls, typeArgs1), field)) + genericSucc.ref[Post, InstanceField[Post]]( + ((t.cls.decl, typeArgs1), field) + ) ) case _ => loc.rewriteDefault() } @@ -193,13 +212,14 @@ case class MonomorphizeClass[Pre <: Generation]() expr match { case deref @ Deref(obj, Ref(field)) => obj.t match { - case TClass(Ref(cls), typeArgs) if typeArgs.nonEmpty => - val typeArgs1 = ctx.topOption.map(_.evalTypes(typeArgs)) - .getOrElse(typeArgs) - instantiate(cls, typeArgs1, false) + case t: TClass[Pre] if t.typeArgs.nonEmpty => + val typeArgs1 = ctx.topOption.map(_.evalTypes(t.typeArgs)) + .getOrElse(t.typeArgs) + instantiate(t.cls.decl, typeArgs1, false) deref.rewrite(ref = - genericSucc - .ref[Post, InstanceField[Post]](((cls, typeArgs1), field)) + genericSucc.ref[Post, InstanceField[Post]]( + ((t.cls.decl, typeArgs1), field) + ) ) case _ => deref.rewriteDefault() } diff --git a/src/rewrite/vct/rewrite/MonomorphizeContractApplicables.scala b/src/rewrite/vct/rewrite/MonomorphizeContractApplicables.scala index 23012710f3..a7175b4b49 100644 --- a/src/rewrite/vct/rewrite/MonomorphizeContractApplicables.scala +++ b/src/rewrite/vct/rewrite/MonomorphizeContractApplicables.scala @@ -46,9 +46,11 @@ case class MonomorphizeContractApplicables[Pre <: Generation]() globalDeclarations.scope { classDeclarations.scope { variables.scope { - allScopes.anyDeclare( - allScopes.anySucceedOnly(app, app.rewrite(typeArgs = Nil)) - ) + localHeapVariables.scope { + allScopes.anyDeclare( + allScopes.anySucceedOnly(app, app.rewrite(typeArgs = Nil)) + ) + } } } } diff --git a/src/rewrite/vct/rewrite/ParBlockEncoder.scala b/src/rewrite/vct/rewrite/ParBlockEncoder.scala index e477579d47..54a74079e9 100644 --- a/src/rewrite/vct/rewrite/ParBlockEncoder.scala +++ b/src/rewrite/vct/rewrite/ParBlockEncoder.scala @@ -151,29 +151,31 @@ case class ParBlockEncoder[Pre <: Generation]() extends Rewriter[Pre] { scale(dispatch(e)) else variables.scope { - val range = quantVars.map(v => - from(v) <= Local[Post](succ(v)) && Local[Post](succ(v)) < to(v) - ).reduceOption[Expr[Post]](And(_, _)).getOrElse(tt) - - e match { - case Forall(bindings, Nil, body) => - Forall( - variables.dispatch(bindings ++ quantVars), - Nil, - range ==> scale(dispatch(body)), - )(body.o) - case s @ Starall(bindings, Nil, body) => - Starall( - variables.dispatch(bindings ++ quantVars), - Nil, - range ==> scale(dispatch(body)), - )(s.blame)(body.o) - case other => - Starall( - variables.dispatch(quantVars), - Nil, - range ==> scale(dispatch(other)), - )(ParBlockNotInjective(block, other))(other.o) + localHeapVariables.scope { + val range = quantVars.map(v => + from(v) <= Local[Post](succ(v)) && Local[Post](succ(v)) < to(v) + ).reduceOption[Expr[Post]](And(_, _)).getOrElse(tt) + + e match { + case Forall(bindings, Nil, body) => + Forall( + variables.dispatch(bindings ++ quantVars), + Nil, + range ==> scale(dispatch(body)), + )(body.o) + case s @ Starall(bindings, Nil, body) => + Starall( + variables.dispatch(bindings ++ quantVars), + Nil, + range ==> scale(dispatch(body)), + )(s.blame)(body.o) + case other => + Starall( + variables.dispatch(quantVars), + Nil, + range ==> scale(dispatch(other)), + )(ParBlockNotInjective(block, other))(other.o) + } } } }) diff --git a/src/rewrite/vct/rewrite/ResolveExpressionSideEffects.scala b/src/rewrite/vct/rewrite/ResolveExpressionSideEffects.scala index 0b344f2a41..ba090ae05e 100644 --- a/src/rewrite/vct/rewrite/ResolveExpressionSideEffects.scala +++ b/src/rewrite/vct/rewrite/ResolveExpressionSideEffects.scala @@ -684,7 +684,19 @@ case class ResolveExpressionSideEffects[Pre <: Generation]() givenMap, yields, ) => - val typ = TClass[Post](succ(cons.cls.decl), classTypeArgs.map(dispatch)) + val typ = + cons.cls.decl match { + case cls: ByReferenceClass[Pre] => + TByReferenceClass[Post]( + succ[Class[Post]](cls), + classTypeArgs.map(dispatch), + ) + case cls: ByValueClass[Pre] => + TByValueClass[Post]( + succ[Class[Post]](cls), + classTypeArgs.map(dispatch), + ) + } val res = new Variable[Post](typ)(ResultVar) variables.succeed(res.asInstanceOf[Variable[Pre]], res) effect( @@ -699,12 +711,25 @@ case class ResolveExpressionSideEffects[Pre <: Generation]() yields.map { case (e, Ref(v)) => (inlined(e), succ(v)) }, )(inv.blame)(e.o) ) - stored(res.get(SideEffectOrigin), TClass(cons.cls, classTypeArgs)) + stored( + res.get(SideEffectOrigin), + cons.cls.decl.classType(classTypeArgs), + ) case NewObject(Ref(cls)) => - val res = new Variable[Post](TClass(succ(cls), Seq()))(ResultVar) + val res = + cls match { + case cls: ByReferenceClass[Pre] => + new Variable[Post]( + TByReferenceClass(succ[Class[Post]](cls), Seq()) + )(ResultVar) + case cls: ByValueClass[Pre] => + new Variable[Post](TByValueClass(succ[Class[Post]](cls), Seq()))( + ResultVar + ) + } variables.succeed(res.asInstanceOf[Variable[Pre]], res) effect(Instantiate[Post](succ(cls), res.get(ResultVar))(e.o)) - stored(res.get(SideEffectOrigin), TClass(cls.ref, Seq())) + stored(res.get(SideEffectOrigin), cls.ref.decl.classType(Seq())) case other => stored(ReInliner().dispatch(rewriteDefault(other)), other.t) } } diff --git a/src/rewrite/vct/rewrite/adt/ImportADT.scala b/src/rewrite/vct/rewrite/adt/ImportADT.scala index 08b35817d7..bc78866cd7 100644 --- a/src/rewrite/vct/rewrite/adt/ImportADT.scala +++ b/src/rewrite/vct/rewrite/adt/ImportADT.scala @@ -71,7 +71,7 @@ case object ImportADT { case TZFraction() => "zfract" case TMap(key, value) => "map$" + typeText(key) + "__" + typeText(value) + "$" - case TClass(Ref(cls), _) => cls.o.getPreferredNameOrElse().camel + case t: TClass[_] => t.cls.decl.o.getPreferredNameOrElse().camel case TVar(Ref(v)) => v.o.getPreferredNameOrElse().camel case TUnion(ts) => "union" + ts.map(typeText).mkString("$", "__", "$") case SilverPartialTAxiomatic(Ref(adt), _) => diff --git a/src/rewrite/vct/rewrite/bip/EncodeBip.scala b/src/rewrite/vct/rewrite/bip/EncodeBip.scala index 52652b5f84..3e08112449 100644 --- a/src/rewrite/vct/rewrite/bip/EncodeBip.scala +++ b/src/rewrite/vct/rewrite/bip/EncodeBip.scala @@ -408,7 +408,7 @@ case class EncodeBip[Pre <: Generation](results: VerificationResults) results.declare(component) implicit val o = DiagnosticOrigin val ref = succ[Class[Post]](classOf(constructor)) - val t = TClass[Post](ref, Seq()) + val t = TByReferenceClass[Post](ref, Seq()) rewritingBipConstructorBody.having(component) { constructorSucc(constructor) = globalDeclarations.declare( new Procedure[Post]( @@ -526,7 +526,7 @@ case class EncodeBip[Pre <: Generation](results: VerificationResults) transitions.flatMap { transition => val v = new Variable[Post]( - TClass(succ[Class[Post]](classOf(transition)), Seq()) + TByReferenceClass(succ[Class[Post]](classOf(transition)), Seq()) )(SynchronizationComponentVariableOrigin( synchronization, componentOf(transition), diff --git a/src/rewrite/vct/rewrite/cfg/Utils.scala b/src/rewrite/vct/rewrite/cfg/Utils.scala index c5a85fc946..73bffad58b 100644 --- a/src/rewrite/vct/rewrite/cfg/Utils.scala +++ b/src/rewrite/vct/rewrite/cfg/Utils.scala @@ -115,7 +115,11 @@ object Utils { } private def get_out_variable[G](cls: Ref[G, Class[G]], o: Origin): Local[G] = - Local(new DirectRef[G, Variable[G]](new Variable(TClass(cls, Seq()))(o)))(o) + Local( + new DirectRef[G, Variable[G]](new Variable(TByReferenceClass(cls, Seq()))( + o + )) + )(o) def find_all_cases[G]( body: Statement[G], diff --git a/src/rewrite/vct/rewrite/exc/EncodeBreakReturn.scala b/src/rewrite/vct/rewrite/exc/EncodeBreakReturn.scala index 66e1de14d1..28d2a8994c 100644 --- a/src/rewrite/vct/rewrite/exc/EncodeBreakReturn.scala +++ b/src/rewrite/vct/rewrite/exc/EncodeBreakReturn.scala @@ -130,7 +130,9 @@ case class EncodeBreakReturn[Pre <: Generation]() extends Rewriter[Pre] { after = Block(Nil), catches = Seq(CatchClause( decl = - new Variable(TClass(breakLabelException.ref(decl), Seq())), + new Variable( + TByReferenceClass(breakLabelException.ref(decl), Seq()) + ), body = Block(Nil), )), ) @@ -147,8 +149,9 @@ case class EncodeBreakReturn[Pre <: Generation]() extends Rewriter[Pre] { case Break(Some(Ref(label))) => val cls = breakLabelException.getOrElseUpdate( label, - globalDeclarations - .declare(new Class[Post](Nil, Nil, Nil, tt)(BreakException)), + globalDeclarations.declare( + new ByReferenceClass[Post](Nil, Nil, Nil, tt)(BreakException) + ), ) Throw(NewObject[Post](cls.ref))(PanicBlame( @@ -156,7 +159,7 @@ case class EncodeBreakReturn[Pre <: Generation]() extends Rewriter[Pre] { )) case Return(result) => - val exc = new Variable[Post](TClass(returnClass.get.ref, Seq())) + val exc = new Variable[Post](returnClass.get.classType(Seq())) Scope( Seq(exc), Block(Seq( @@ -196,13 +199,16 @@ case class EncodeBreakReturn[Pre <: Generation]() extends Rewriter[Pre] { ReturnField ) val returnClass = - new Class[Post](Nil, Seq(returnField), Nil, tt)( - ReturnClass - ) + new ByReferenceClass[Post]( + Nil, + Seq(returnField), + Nil, + tt, + )(ReturnClass) globalDeclarations.declare(returnClass) val caughtReturn = - new Variable[Post](TClass(returnClass.ref, Seq())) + new Variable[Post](returnClass.classType(Seq())) TryCatchFinally( body = BreakReturnToException( diff --git a/src/rewrite/vct/rewrite/lang/LangCPPToCol.scala b/src/rewrite/vct/rewrite/lang/LangCPPToCol.scala index e66592de4b..debd341db5 100644 --- a/src/rewrite/vct/rewrite/lang/LangCPPToCol.scala +++ b/src/rewrite/vct/rewrite/lang/LangCPPToCol.scala @@ -1348,7 +1348,8 @@ case class LangCPPToCol[Pre <: Generation](rw: LangSpecificToCol[Pre]) // Create a class that can be used to create a 'this' object // It will be linked to the class made near the end of this method. - val preEventClass: Class[Pre] = new Class(Nil, Nil, Nil, tt)(commandGroup.o) + val preEventClass: Class[Pre] = + new ByValueClass(Nil, Nil, Nil)(commandGroup.o) this.currentThis = Some( rw.dispatch(ThisObject[Pre](preEventClass.ref)(preEventClass.o)) ) @@ -1475,8 +1476,9 @@ case class LangCPPToCol[Pre <: Generation](rw: LangSpecificToCol[Pre]) )(KernelLambdaRunMethodBlame(kernelDeclaration))(commandGroup.o) // Create the surrounding class + // cl::sycl::event has a default copy constructor hence a ByValueClass val postEventClass = - new Class[Post]( + new ByValueClass[Post]( typeArgs = Seq(), decls = currentKernelType.get.getRangeFields ++ @@ -1484,13 +1486,12 @@ case class LangCPPToCol[Pre <: Generation](rw: LangSpecificToCol[Pre]) .flatMap(acc => acc.instanceField +: acc.rangeIndexFields) ++ Seq(kernelRunner), supports = Seq(), - intrinsicLockInvariant = tt, )(commandGroup.o.where(name = "SYCL_EVENT_CLASS")) rw.globalDeclarations.succeed(preEventClass, postEventClass) // Create a variable to refer to the class instance val eventClassRef = - new Variable[Post](TClass(postEventClass.ref, Seq()))( + new Variable[Post](TByValueClass(postEventClass.ref, Seq()))( commandGroup.o.where(name = "sycl_event_ref") ) // Store the class ref and read-write accessors to be used when the kernel is done running @@ -1976,7 +1977,7 @@ case class LangCPPToCol[Pre <: Generation](rw: LangSpecificToCol[Pre]) preClass: Class[Pre], commandGroupO: Origin, ): Procedure[Post] = { - val t = rw.dispatch(TClass[Pre](preClass.ref, Seq())) + val t = rw.dispatch(TByValueClass[Pre](preClass.ref, Seq())) rw.globalDeclarations.declare( withResult((result: Result[Post]) => { val constructorPostConditions: mutable.Buffer[Expr[Post]] = @@ -2142,22 +2143,24 @@ case class LangCPPToCol[Pre <: Generation](rw: LangSpecificToCol[Pre]) scale(cond) else rw.variables.scope { - val range = quantVars.map(v => - rangesMap(v)._1 <= Local[Post](v.ref) && - Local[Post](v.ref) < rangesMap(v)._2 - ).reduceOption[Expr[Post]](And(_, _)).getOrElse(tt) - - cond match { - case Forall(bindings, Nil, body) => - Forall(bindings ++ quantVars, Nil, range ==> scale(body)) - case s @ Starall(bindings, Nil, body) => - Starall(bindings ++ quantVars, Nil, range ==> scale(body))( - s.blame - ) - case other => - Starall(quantVars.toSeq, Nil, range ==> scale(other))( - ParBlockNotInjective(block, other) - ) + rw.localHeapVariables.scope { + val range = quantVars.map(v => + rangesMap(v)._1 <= Local[Post](v.ref) && + Local[Post](v.ref) < rangesMap(v)._2 + ).reduceOption[Expr[Post]](And(_, _)).getOrElse(tt) + + cond match { + case Forall(bindings, Nil, body) => + Forall(bindings ++ quantVars, Nil, range ==> scale(body)) + case s @ Starall(bindings, Nil, body) => + Starall(bindings ++ quantVars, Nil, range ==> scale(body))( + s.blame + ) + case other => + Starall(quantVars.toSeq, Nil, range ==> scale(other))( + ParBlockNotInjective(block, other) + ) + } } } }) diff --git a/src/rewrite/vct/rewrite/lang/LangCToCol.scala b/src/rewrite/vct/rewrite/lang/LangCToCol.scala index bcd6d89252..f4ca25352c 100644 --- a/src/rewrite/vct/rewrite/lang/LangCToCol.scala +++ b/src/rewrite/vct/rewrite/lang/LangCToCol.scala @@ -192,25 +192,6 @@ case object LangCToCol { } } - case class StructCopyFailed( - assign: PreAssignExpression[_], - field: InstanceField[_], - ) extends Blame[InsufficientPermission] { - override def blame(error: InsufficientPermission): Unit = { - assign.blame.blame(CopyStructFailed(assign, Referrable.originName(field))) - } - } - - case class StructCopyBeforeCallFailed( - inv: CInvocation[_], - field: InstanceField[_], - ) extends Blame[InsufficientPermission] { - override def blame(error: InsufficientPermission): Unit = { - inv.blame - .blame(CopyStructFailedBeforeCall(inv, Referrable.originName(field))) - } - } - case class VectorBoundFailed(subscript: AmbiguousSubscript[_]) extends Blame[InvocationFailure] { override def blame(error: InvocationFailure): Unit = @@ -273,6 +254,8 @@ case class LangCToCol[Pre <: Generation](rw: LangSpecificToCol[Pre]) SuccessionMap() val cNameSuccessor: SuccessionMap[CNameTarget[Pre], Variable[Post]] = SuccessionMap() + val cLocalHeapNameSuccessor: SuccessionMap[CNameTarget[Pre], LocalHeapVariable[Post]] = + SuccessionMap() val cGlobalNameSuccessor : SuccessionMap[CNameTarget[Pre], HeapVariable[Post]] = SuccessionMap() val cStructSuccessor: SuccessionMap[CGlobalDeclaration[Pre], Class[Post]] = @@ -303,7 +286,7 @@ case class LangCToCol[Pre <: Generation](rw: LangSpecificToCol[Pre]) private var kernelSpecifier: Option[CGpgpuKernelSpecifier[Pre]] = None private def CStructOrigin(sdecl: CStructDeclaration[_]): Origin = - sdecl.o.sourceName(sdecl.name.get) + sdecl.o.sourceName(sdecl.name.get).withContent(TypeName("struct")) private def CStructFieldOrigin(cdecl: CDeclarator[_]): Origin = cdecl.o.sourceName(nameFromDeclarator(cdecl)) @@ -999,7 +982,7 @@ case class LangCToCol[Pre <: Generation](rw: LangSpecificToCol[Pre]) case _ => throw WrongStructType(decl) } val newStruct = - new Class[Post]( + new ByValueClass[Post]( Seq(), rw.classDeclarations.collect { decls.foreach { fieldDecl => @@ -1019,7 +1002,6 @@ case class LangCToCol[Pre <: Generation](rw: LangSpecificToCol[Pre]) } }._1, Seq(), - tt[Post], )(CStructOrigin(sdecl)) rw.globalDeclarations.declare(newStruct) @@ -1163,21 +1145,14 @@ case class LangCToCol[Pre <: Generation](rw: LangSpecificToCol[Pre]) implicit val o: Origin = init.o val targetClass: Class[Post] = cStructSuccessor(ref.decl) - val t = TClass[Post](targetClass.ref, Seq()) + val t = TByValueClass[Post](targetClass.ref, Seq()) - val v = new Variable[Post](t)(o.sourceName(info.name)) - cNameSuccessor(RefCLocalDeclaration(decl, 0)) = v - - val initialVal = init.init.map(i => - createStructCopy( - rw.dispatch(i), - ref.decl, - (f: InstanceField[_]) => - PanicBlame("Cannot fail due to insufficient perm"), - ) - ).getOrElse(NewObject[Post](targetClass.ref)) + val v = new LocalHeapVariable[Post](t)(o.sourceName(info.name)) + cLocalHeapNameSuccessor(RefCLocalDeclaration(decl, 0)) = v - Block(Seq(LocalDecl(v), assignLocal(v.get, initialVal))) + if (init.init.isDefined) { + Block(Seq(HeapLocalDecl(v), assignHeapLocal(v.get, rw.dispatch(init.init.get)))) + } else { HeapLocalDecl(v) } } def rewriteLocal(decl: CLocalDeclaration[Pre]): Statement[Post] = { @@ -1340,6 +1315,7 @@ case class LangCToCol[Pre <: Generation](rw: LangSpecificToCol[Pre]) DerefHeapVariable[Post](cGlobalNameSuccessor.ref(ref))(local.blame) case Some(_) => throw NotAValue(local) } + case ref: RefCLocalDeclaration[Pre] if cLocalHeapNameSuccessor.contains(ref) => HeapLocal(cLocalHeapNameSuccessor.ref(ref)) case ref: RefCLocalDeclaration[Pre] => Local(cNameSuccessor.ref(ref)) case _: RefCudaVec[Pre] => throw NotAValue(local) } @@ -1460,59 +1436,6 @@ case class LangCToCol[Pre <: Generation](rw: LangSpecificToCol[Pre]) foldStar(newFieldPerms) } - def createStructCopy( - value: Expr[Post], - struct: CGlobalDeclaration[Pre], - blame: InstanceField[_] => Blame[InsufficientPermission], - )(implicit o: Origin): Expr[Post] = { - val targetClass: Class[Post] = cStructSuccessor(struct) - val t = TClass[Post](targetClass.ref, Seq()) - - // Assign a new variable towards the value, such that methods do not get executed multiple times. - val vValue = new Variable[Post](t) - // The copy of the value - val vCopy = new Variable[Post](t) - - val fieldAssigns = targetClass.declarations.collect { - case field: InstanceField[Post] => - val ref: Ref[Post, InstanceField[Post]] = field.ref - assignField( - vCopy.get, - ref, - Deref[Post](vValue.get, field.ref)(blame(field)), - PanicBlame("Assignment should work"), - ) - } - - With( - Block( - Seq( - LocalDecl(vCopy), - LocalDecl(vValue), - assignLocal(vValue.get, value), - assignLocal(vCopy.get, NewObject[Post](targetClass.ref)), - ) ++ fieldAssigns - ), - vCopy.get, - ) - } - - def assignStruct(assign: PreAssignExpression[Pre]): Expr[Post] = { - getBaseType(assign.target.t) match { - case CTStruct(ref) => - val copy = - createStructCopy( - rw.dispatch(assign.value), - ref.decl, - (f: InstanceField[_]) => StructCopyFailed(assign, f), - )(assign.o) - PreAssignExpression(rw.dispatch(assign.target), copy)(AssignLocalOk)( - assign.o - ) - case _ => throw WrongStructType(assign.target) - } - } - def createUpdateVectorFunction(size: Int): Function[Post] = { implicit val o: Origin = Origin(Seq(LabelContext("vector update method"))) /* for instance for size 4: @@ -1748,18 +1671,7 @@ case class LangCToCol[Pre <: Generation](rw: LangSpecificToCol[Pre]) case _ => } - // Create copy for any direct structure arguments - val newArgs = args.map(a => - getBaseType(a.t) match { - case CTStruct(ref) => - createStructCopy( - rw.dispatch(a), - ref.decl, - (f: InstanceField[_]) => StructCopyBeforeCallFailed(inv, f), - )(a.o) - case _ => rw.dispatch(a) - } - ) + val newArgs = args.map(a => rw.dispatch(a)) implicit val o: Origin = inv.o inv.ref.get match { @@ -1998,6 +1910,6 @@ case class LangCToCol[Pre <: Generation](rw: LangSpecificToCol[Pre]) def structType(t: CTStruct[Pre]): Type[Post] = { val targetClass = new LazyRef[Post, Class[Post]](cStructSuccessor(t.ref.decl)) - TClass[Post](targetClass, Seq())(t.o) + TByValueClass[Post](targetClass, Seq())(t.o) } } diff --git a/src/rewrite/vct/rewrite/lang/LangJavaToCol.scala b/src/rewrite/vct/rewrite/lang/LangJavaToCol.scala index 7a65f46ca6..8b73dce94d 100644 --- a/src/rewrite/vct/rewrite/lang/LangJavaToCol.scala +++ b/src/rewrite/vct/rewrite/lang/LangJavaToCol.scala @@ -275,7 +275,7 @@ case class LangJavaToCol[Pre <: Generation](rw: LangSpecificToCol[Pre]) case cons: JavaConstructor[Pre] => logger.debug(s"Constructor for ${cons.o.inlineContextText}") implicit val o: Origin = cons.o - val t = TClass(ref, Seq()) + val t = TByReferenceClass(ref, Seq()) val `this` = ThisObject(ref) val results = currentJavaClass.top.modifiers.collect { @@ -429,7 +429,7 @@ case class LangJavaToCol[Pre <: Generation](rw: LangSpecificToCol[Pre]) val instanceClass = rw.currentThis.having(ThisObject(javaInstanceClassSuccessor.ref(cls))) { - new Class[Post]( + new ByReferenceClass[Post]( rw.variables.dispatch(cls.typeParams)(rw), rw.classDeclarations.collect { makeJavaClass( @@ -454,7 +454,7 @@ case class LangJavaToCol[Pre <: Generation](rw: LangSpecificToCol[Pre]) if (staticDecls.nonEmpty) { val staticsClass = - new Class[Post]( + new ByReferenceClass[Post]( Seq(), rw.classDeclarations.collect { rw.currentThis @@ -472,7 +472,7 @@ case class LangJavaToCol[Pre <: Generation](rw: LangSpecificToCol[Pre]) )(JavaStaticsClassOrigin(cls)) rw.globalDeclarations.declare(staticsClass) - val t = TClass[Post](staticsClass.ref, Seq()) + val t = TByReferenceClass[Post](staticsClass.ref, Seq()) val singleton = withResult((res: Result[Post]) => function( AbstractApplicable, @@ -754,7 +754,7 @@ case class LangJavaToCol[Pre <: Generation](rw: LangSpecificToCol[Pre]) def classType(t: JavaTClass[Pre]): Type[Post] = t.ref.decl match { case classOrInterface: JavaClassOrInterface[Pre] => - TClass( + TByReferenceClass( javaInstanceClassSuccessor.ref(classOrInterface), t.typeArgs.map(rw.dispatch), ) diff --git a/src/rewrite/vct/rewrite/lang/LangPVLToCol.scala b/src/rewrite/vct/rewrite/lang/LangPVLToCol.scala index 4952f2eae4..6db99e77dc 100644 --- a/src/rewrite/vct/rewrite/lang/LangPVLToCol.scala +++ b/src/rewrite/vct/rewrite/lang/LangPVLToCol.scala @@ -179,7 +179,7 @@ case class LangPVLToCol[Pre <: Generation]( val PVLNew(t, typeArgs, args, givenMap, yields) = inv val classTypeArgs = t match { - case TClass(_, typeArgs) => typeArgs + case t: TClass[Pre] => t.typeArgs case _ => Seq() } implicit val o: Origin = inv.o diff --git a/src/rewrite/vct/rewrite/lang/LangSpecificToCol.scala b/src/rewrite/vct/rewrite/lang/LangSpecificToCol.scala index 213997930e..488782e83b 100644 --- a/src/rewrite/vct/rewrite/lang/LangSpecificToCol.scala +++ b/src/rewrite/vct/rewrite/lang/LangSpecificToCol.scala @@ -206,7 +206,13 @@ case class LangSpecificToCol[Pre <: Generation]( pvl.maybeDeclareDefaultConstructor(cls) }._1 - globalDeclarations.succeed(cls, cls.rewrite(decls = decls)) + globalDeclarations.succeed( + cls, + cls match { + case cls: ByReferenceClass[Pre] => cls.rewrite(decls = decls) + case cls: ByValueClass[Pre] => cls.rewrite(decls = decls) + }, + ) } } @@ -369,10 +375,6 @@ case class LangSpecificToCol[Pre <: Generation]( case _ => } assign.target.t match { - case CPrimitiveType(specs) if specs.collectFirst { - case CSpecificationType(_: CTStruct[Pre]) => () - }.isDefined => - c.assignStruct(assign) case CPPPrimitiveType(_) => cpp.preAssignExpr(assign) case _ => rewriteDefault(assign) } diff --git a/src/rewrite/vct/rewrite/lang/LangTypesToCol.scala b/src/rewrite/vct/rewrite/lang/LangTypesToCol.scala index 6b7f9fe8eb..adfbeb4be1 100644 --- a/src/rewrite/vct/rewrite/lang/LangTypesToCol.scala +++ b/src/rewrite/vct/rewrite/lang/LangTypesToCol.scala @@ -88,7 +88,10 @@ case class LangTypesToCol[Pre <: Generation]() extends Rewriter[Pre] { case t @ PVLNamedType(_, typeArgs) => t.ref.get match { case spec: SpecTypeNameTarget[Pre] => specType(spec, typeArgs) - case RefClass(decl) => TClass(succ(decl), typeArgs.map(dispatch)) + case RefClass(decl: ByReferenceClass[Pre]) => + TByReferenceClass(succ[Class[Post]](decl), typeArgs.map(dispatch)) + case RefClass(decl: ByValueClass[Pre]) => + TByValueClass(succ[Class[Post]](decl), typeArgs.map(dispatch)) } case t @ CPrimitiveType(specs) => dispatch(C.getPrimitiveType(specs, context = Some(t))) diff --git a/src/rewrite/vct/rewrite/lang/NoSupportSelfLoop.scala b/src/rewrite/vct/rewrite/lang/NoSupportSelfLoop.scala index de0cfe57c6..f59b38fcb1 100644 --- a/src/rewrite/vct/rewrite/lang/NoSupportSelfLoop.scala +++ b/src/rewrite/vct/rewrite/lang/NoSupportSelfLoop.scala @@ -13,7 +13,15 @@ case object NoSupportSelfLoop extends RewriterBuilder { case class NoSupportSelfLoop[Pre <: Generation]() extends Rewriter[Pre] { override def dispatch(decl: Declaration[Pre]): Unit = decl match { - case cls: Class[Pre] => + case cls: ByReferenceClass[Pre] => + globalDeclarations.succeed( + cls, + cls.rewrite(supports = + cls.supports.filter(_.asClass.get.cls.decl != cls) + .map(_.rewriteDefault()) + ), + ) + case cls: ByValueClass[Pre] => globalDeclarations.succeed( cls, cls.rewrite(supports = diff --git a/src/rewrite/vct/rewrite/util/Extract.scala b/src/rewrite/vct/rewrite/util/Extract.scala index 95ef8f02d9..35ba7fe755 100644 --- a/src/rewrite/vct/rewrite/util/Extract.scala +++ b/src/rewrite/vct/rewrite/util/Extract.scala @@ -84,7 +84,9 @@ case class Extract[G]() { t -> Local( getOrElseUpdate( free, - new Variable(extract(TClass(t.cls, Seq())))(ExtractOrigin("this")), + new Variable(extract(t.cls.decl.classType(Seq())))(ExtractOrigin( + "this" + )), ).ref[Variable[G]] )(ExtractOrigin("")) case free @ FreeThisModel(t) => diff --git a/src/rewrite/vct/rewrite/veymont/EncodeChannels.scala b/src/rewrite/vct/rewrite/veymont/EncodeChannels.scala index 5a0d2dc111..6a229562c3 100644 --- a/src/rewrite/vct/rewrite/veymont/EncodeChannels.scala +++ b/src/rewrite/vct/rewrite/veymont/EncodeChannels.scala @@ -5,6 +5,7 @@ import hre.util.ScopedStack import vct.col.ast.{ Assign, Block, + ByValueClass, ChorStatement, Choreography, Class, @@ -23,6 +24,7 @@ import vct.col.ast.{ Program, Scope, Statement, + TByValueClass, TClass, TVar, Type, @@ -81,7 +83,7 @@ case class EncodeChannels[Pre <: Generation](importer: ImportADTImporter) }.get def channelType(comm: Communicate[Pre]): Type[Post] = - TClass[Post](channelClassSucc.ref(comm), Seq()) + TByValueClass[Post](channelClassSucc.ref(comm), Seq()) val currentCommunicate = ScopedStack[Communicate[Pre]]() val currentMsgTVar = ScopedStack[Variable[Pre]]() @@ -159,7 +161,7 @@ case class EncodeChannels[Pre <: Generation](importer: ImportADTImporter) }).succeed(chor) } - case cls: Class[Pre] if isEndpointClass(cls) => + case cls: ByValueClass[Pre] if isEndpointClass(cls) => cls.rewrite(decls = classDeclarations.collect { cls.decls.foreach(dispatch) @@ -174,13 +176,15 @@ case class EncodeChannels[Pre <: Generation](importer: ImportADTImporter) }._1 ).succeed(cls) - case cls: Class[Pre] if cls == genericChannelClass => + case cls: ByValueClass[Pre] if cls == genericChannelClass => globalDeclarations.scope { classDeclarations.scope { variables.scope { - currentMsgTVar.having(cls.typeArgs.head) { - channelClassSucc(currentCommunicate.top) = cls - .rewrite(typeArgs = Seq()).succeed(cls) + localHeapVariables.scope { + currentMsgTVar.having(cls.typeArgs.head) { + channelClassSucc(currentCommunicate.top) = cls + .rewrite(typeArgs = Seq()).succeed(cls) + } } } } diff --git a/src/rewrite/vct/rewrite/veymont/EncodeChoreography.scala b/src/rewrite/vct/rewrite/veymont/EncodeChoreography.scala index 76f4f9513c..a22aff6af8 100644 --- a/src/rewrite/vct/rewrite/veymont/EncodeChoreography.scala +++ b/src/rewrite/vct/rewrite/veymont/EncodeChoreography.scala @@ -31,7 +31,7 @@ import vct.col.ast.{ Scope, Sender, Statement, - TClass, + TByReferenceClass, TVoid, ThisChoreography, Variable, @@ -257,9 +257,9 @@ case class EncodeChoreography[Pre <: Generation]() currentInstanceMethod.having(method) { for (endpoint <- prog.endpoints) { endpointSucc((mode, endpoint)) = - new Variable(TClass(succ[Class[Post]](endpoint.cls.decl), Seq()))( - endpoint.o - ) + new Variable( + TByReferenceClass(succ[Class[Post]](endpoint.cls.decl), Seq()) + )(endpoint.o) } prog.params.foreach(_.drop()) diff --git a/src/rewrite/vct/rewrite/veymont/EncodeChoreographyParameters.scala b/src/rewrite/vct/rewrite/veymont/EncodeChoreographyParameters.scala index e86486d36c..38f0f9c535 100644 --- a/src/rewrite/vct/rewrite/veymont/EncodeChoreographyParameters.scala +++ b/src/rewrite/vct/rewrite/veymont/EncodeChoreographyParameters.scala @@ -4,8 +4,8 @@ import com.typesafe.scalalogging.LazyLogging import hre.util.ScopedStack import vct.col.ast.{ Block, + ByReferenceClass, Choreography, - Class, Declaration, Endpoint, EndpointName, @@ -44,8 +44,10 @@ case class EncodeChoreographyParameters[Pre <: Generation]() case p: Choreography[Pre] => p } lazy val allEndpoints = choreographies.flatMap { _.endpoints } - lazy val endpointOfClass: Map[Class[Pre], Endpoint[Pre]] = - allEndpoints.map { endpoint => (endpoint.cls.decl, endpoint) }.toMap + lazy val endpointOfClass: Map[ByReferenceClass[Pre], Endpoint[Pre]] = + allEndpoints.map { endpoint => + (endpoint.cls.decl.asInstanceOf[ByReferenceClass[Pre]], endpoint) + }.toMap lazy val choreographyOfEndpoint: Map[Endpoint[Pre], Choreography[Pre]] = choreographies.flatMap { chor => chor.endpoints.map { ep => (ep, chor) } } .toMap @@ -83,7 +85,7 @@ case class EncodeChoreographyParameters[Pre <: Generation]() }), ) } - case cls: Class[Pre] if endpointOfClass.contains(cls) => + case cls: ByReferenceClass[Pre] if endpointOfClass.contains(cls) => val endpoint = endpointOfClass(cls) val chor = choreographyOfEndpoint(endpoint) implicit val o = chor.o diff --git a/src/rewrite/vct/rewrite/veymont/GenerateChoreographyPermissions.scala b/src/rewrite/vct/rewrite/veymont/GenerateChoreographyPermissions.scala index b66fa3c90b..c074286515 100644 --- a/src/rewrite/vct/rewrite/veymont/GenerateChoreographyPermissions.scala +++ b/src/rewrite/vct/rewrite/veymont/GenerateChoreographyPermissions.scala @@ -230,7 +230,7 @@ case class GenerateChoreographyPermissions[Pre <: Generation]( transitivePerm(Result[Post](anySucc(app)), app.returnType) def classPerm(cls: Class[Pre]): Expr[Post] = - transitivePerm(ThisObject[Post](succ(cls))(cls.o), TClass(cls.ref, Seq()))( + transitivePerm(ThisObject[Post](succ(cls))(cls.o), cls.classType(Seq()))( cls.o ) @@ -276,17 +276,18 @@ case class GenerateChoreographyPermissions[Pre <: Generation]( u, )), ) - case TClass(Ref(cls), _) if !generatingClasses.contains(cls) => - generatingClasses.having(cls) { - foldStar(cls.collect { case f: InstanceField[Pre] => + case t: TClass[Pre] if !generatingClasses.contains(t.cls.decl) => + generatingClasses.having(t.cls.decl) { + foldStar(t.cls.decl.collect { case f: InstanceField[Pre] => fieldTransitivePerm(e, f)(f.o) }) } - case TClass(Ref(cls), _) => + case t: TByReferenceClass[Pre] => // The class we are generating permission for has already been encountered when going through the chain // of fields. So we cut off the computation logger.warn( - s"Not generating permissions for recursive occurrence of ${cls.o.getPreferredNameOrElse().ucamel}. Circular datastructures are not supported by permission generation" + s"Not generating permissions for recursive occurrence of ${t.cls.decl + .o.getPreferredNameOrElse().ucamel}. Circular datastructures are not supported by permission generation" ) tt case _ => tt diff --git a/src/rewrite/vct/rewrite/veymont/GenerateImplementation.scala b/src/rewrite/vct/rewrite/veymont/GenerateImplementation.scala index 06e100d262..dbff2b2b99 100644 --- a/src/rewrite/vct/rewrite/veymont/GenerateImplementation.scala +++ b/src/rewrite/vct/rewrite/veymont/GenerateImplementation.scala @@ -10,6 +10,7 @@ import vct.col.ast.{ Block, BooleanValue, Branch, + ByReferenceClass, ChorBranch, ChorGuard, ChorLoop, @@ -55,6 +56,7 @@ import vct.col.ast.{ Star, Statement, TClass, + TByReferenceClass, TVeyMontChannel, TVoid, ThisChoreography, @@ -211,7 +213,7 @@ case class GenerateImplementation[Pre <: Generation]() override def dispatch(decl: Declaration[Pre]): Unit = { decl match { case p: Procedure[Pre] => super.dispatch(p) - case cls: Class[Pre] if isEndpointClass(cls) => + case cls: ByReferenceClass[Pre] if isEndpointClass(cls) => val chor = choreographyOf(cls) val endpoint = endpointOf(cls) currentThis.having(ThisObject[Post](succ(cls))(cls.o)) { @@ -457,7 +459,7 @@ case class GenerateImplementation[Pre <: Generation]() seqProg.endpoints.foreach(thread => { val threadField = new InstanceField[Post]( - TClass(givenClassSucc.ref(thread.t), Seq()), + TByReferenceClass(givenClassSucc.ref(thread.t), Seq()), Nil, )(thread.o) val channelFields = getChannelFields( @@ -476,16 +478,16 @@ case class GenerateImplementation[Pre <: Generation]() }) } - private def dispatchGivenClass(c: Class[Pre]): Class[Post] = { + private def dispatchGivenClass(c: ByReferenceClass[Pre]): Class[Post] = { val rw = GivenClassRewriter() val gc = c.rewrite(decls = classDeclarations.collect { - (givenClassConstrSucc.get(TClass(c.ref, Seq())).get +: c.declarations) - .foreach(d => rw.dispatch(d)) + (givenClassConstrSucc.get(TByReferenceClass(c.ref, Seq())).get +: + c.declarations).foreach(d => rw.dispatch(d)) }._1 )(rw) - givenClassSucc.update(TClass(c.ref, Seq()), gc) + givenClassSucc.update(TByReferenceClass(c.ref, Seq()), gc) gc } @@ -550,7 +552,7 @@ case class GenerateImplementation[Pre <: Generation]() else rewriteDefault(l) case t: ThisObject[Pre] => - val thisClassType = TClass(t.cls, Seq()) + val thisClassType = TByReferenceClass(t.cls, Seq()) if ( rewritingConstr.nonEmpty && rewritingConstr.top._2 == thisClassType ) @@ -623,7 +625,7 @@ case class GenerateImplementation[Pre <: Generation]() val threadRun = getThreadRunMethod(threadRes.runMethod) classDeclarations.scope { val threadClass = - new Class[Post]( + new ByReferenceClass[Post]( Seq(), (threadRes.threadField +: threadRes.channelFields.values.toSeq) ++ (threadConstr +: threadRun +: threadMethods), diff --git a/src/rewrite/vct/rewrite/veymont/SpecializeEndpointClasses.scala b/src/rewrite/vct/rewrite/veymont/SpecializeEndpointClasses.scala index 177c4064b5..f75250f037 100644 --- a/src/rewrite/vct/rewrite/veymont/SpecializeEndpointClasses.scala +++ b/src/rewrite/vct/rewrite/veymont/SpecializeEndpointClasses.scala @@ -11,6 +11,7 @@ import vct.col.ast.{ Block, BooleanValue, Branch, + ByReferenceClass, ChorGuard, ChorRun, ChorStatement, @@ -140,7 +141,7 @@ case class SpecializeEndpointClasses[Pre <: Generation]() } val wrapperClass = - new Class[Post]( + new ByReferenceClass[Post]( typeArgs = Seq(), supports = Seq(), intrinsicLockInvariant = tt, diff --git a/test/main/vct/helper/SimpleProgramGenerator.scala b/test/main/vct/helper/SimpleProgramGenerator.scala index f0587c3abd..3aee073fe9 100644 --- a/test/main/vct/helper/SimpleProgramGenerator.scala +++ b/test/main/vct/helper/SimpleProgramGenerator.scala @@ -20,7 +20,7 @@ object SimpleProgramGenerator { val contract1 = generateSimpleApplicableContract[G]() val blame1 = origin val method1 = new InstanceMethod(TVoid(), Nil, Nil, Nil, Option(body), contract1)(blame1) - val classNode1 = new Class(Nil, Seq(method1), Nil, tt) + val classNode1 = new ByReferenceClass(Nil, Seq(method1), Nil, tt) Program(Seq(classNode1))(DiagnosticOrigin) } diff --git a/test/main/vct/test/integration/examples/CSpec.scala b/test/main/vct/test/integration/examples/CSpec.scala index d4cfd0de4e..b5af488aab 100644 --- a/test/main/vct/test/integration/examples/CSpec.scala +++ b/test/main/vct/test/integration/examples/CSpec.scala @@ -310,7 +310,7 @@ class CSpec extends VercorsSpec { } """ - vercors should fail withCode "copyStructFailedBeforeCall" using silicon in "Insufficient permission for field x to copy struct before call" c + vercors should fail withCode "copyClassFailedBeforeCall" using silicon in "Insufficient permission for field x to copy struct before call" c """ struct d { int x; @@ -328,7 +328,7 @@ class CSpec extends VercorsSpec { } """ - vercors should fail withCode "copyStructFailed" using silicon in "Insufficient permission for field x to copy struct" c + vercors should fail withCode "copyClassFailed" using silicon in "Insufficient permission for field x to copy struct" c """ struct d { int x; From b01df4d72ab5b35b9a9848e90d4dc86e1c109502 Mon Sep 17 00:00:00 2001 From: Alexander Stekelenburg Date: Tue, 11 Jun 2024 15:43:46 +0200 Subject: [PATCH 02/26] Use ADT encoding for ByValueClasses --- examples/concepts/c/structs.c | 19 +- src/col/vct/col/ast/Node.scala | 31 +- .../alloc/NewNonNullPointerArrayImpl.scala | 14 + .../coercion/CoerceNonNullPointerImpl.scala | 9 + .../location/ByValueClassLocationImpl.scala | 10 + .../col/ast/type/TNonNullPointerImpl.scala | 16 + src/col/vct/col/origin/Blame.scala | 9 +- src/col/vct/col/resolve/Resolve.scala | 1 + .../vct/col/typerules/CoercingRewriter.scala | 4 + src/col/vct/col/typerules/CoercionUtils.scala | 5 + src/col/vct/col/util/AstBuildHelpers.scala | 15 +- src/main/vct/main/stages/Transformation.scala | 9 +- src/rewrite/vct/rewrite/ClassToRef.scala | 262 ++++++++++++++-- .../vct/rewrite/DisambiguateLocation.scala | 3 +- .../vct/rewrite/EncodeArrayValues.scala | 60 +++- .../vct/rewrite/EncodeByValueClass.scala | 249 ---------------- .../vct/rewrite/LowerLocalHeapVariables.scala | 111 +++++++ .../vct/rewrite/PrepareByValueClass.scala | 279 ++++++++++++++++++ .../ResolveExpressionSideEffects.scala | 2 + src/rewrite/vct/rewrite/TrivialAddrOf.scala | 4 +- .../vct/rewrite/VariableToPointer.scala | 221 ++++++++++++++ .../vct/rewrite/adt/ImportPointer.scala | 105 +++---- src/rewrite/vct/rewrite/lang/LangCToCol.scala | 57 ++-- .../vct/rewrite/lang/LangSpecificToCol.scala | 22 +- 24 files changed, 1117 insertions(+), 400 deletions(-) create mode 100644 src/col/vct/col/ast/expr/heap/alloc/NewNonNullPointerArrayImpl.scala create mode 100644 src/col/vct/col/ast/family/coercion/CoerceNonNullPointerImpl.scala create mode 100644 src/col/vct/col/ast/family/location/ByValueClassLocationImpl.scala create mode 100644 src/col/vct/col/ast/type/TNonNullPointerImpl.scala delete mode 100644 src/rewrite/vct/rewrite/EncodeByValueClass.scala create mode 100644 src/rewrite/vct/rewrite/LowerLocalHeapVariables.scala create mode 100644 src/rewrite/vct/rewrite/PrepareByValueClass.scala create mode 100644 src/rewrite/vct/rewrite/VariableToPointer.scala diff --git a/examples/concepts/c/structs.c b/examples/concepts/c/structs.c index 44882c9ac6..886ed073f5 100644 --- a/examples/concepts/c/structs.c +++ b/examples/concepts/c/structs.c @@ -21,8 +21,8 @@ struct linked_list{ /*@ context p != NULL ** Perm(p, write); - context Perm(p->x, write); - context Perm(p->y, write); + context Perm(&p->x, write); + context Perm(&p->y, write); ensures p->x == 0; ensures p->y == 0; ensures \old(*p) == *p; @@ -44,14 +44,15 @@ void alter_struct_1(struct point *p){ } /*@ - context Perm(p.x, 1\1); - context Perm(p.y, 1\1); + context Perm(&p.x, 1\1); + context Perm(&p.y, 1\1); @*/ void alter_copy_struct(struct point p){ p.x = 0; p.y = 0; } +// TODO: Should be auto-generated /*@ context Perm(p, 1\1); @*/ @@ -75,7 +76,7 @@ int avr_x(struct triangle *r){ requires inp != NULL && \pointer_length(inp) >= n; requires (\forall* int i; 0 <= i && i < n; Perm(&inp[i], 1\10)); requires (\forall int i, int j; 0<=i && i {:inp[i]:} != {:inp[j]:}); - requires (\forall* int i; 0 <= i && i < n; Perm(inp[i].x, 1\10)); + requires (\forall* int i; 0 <= i && i < n; Perm(&inp[i].x, 1\10)); ensures |\result| == n; ensures (\forall int i; 0 <= i && i < n; \result[i] == inp[i].x); //ensures n>0 ==> \result == inp_to_seq(inp, n-1) + [inp[n-1].x]; @@ -132,7 +133,7 @@ int main(){ struct point *pp; pp = &p; - //@ assert (pp[0] != NULL ); + /* //@ assert (pp[0] != NULL ); */ assert (pp != NULL ); p.x = 1; @@ -163,9 +164,13 @@ int main(){ struct polygon pol, *ppols; ppols = &pol; pol.ps = ps; + //@ assert Perm(&ppols->ps[0], write); + //@ assert Perm(&ppols->ps[1], write); + //@ assert Perm(&ppols->ps[2], write); + //@ assert (\forall* int i; 0<=i && i<3; Perm(&ppols->ps[i], write)); int avr_pol = avr_x_pol(ppols, 3); // assert sum_seq(inp_to_seq(ppols->ps, 3)) == 6; assert(avr_pol == 2); return 0; -} \ No newline at end of file +} diff --git a/src/col/vct/col/ast/Node.scala b/src/col/vct/col/ast/Node.scala index 67e3e06e5a..c46d938b5b 100644 --- a/src/col/vct/col/ast/Node.scala +++ b/src/col/vct/col/ast/Node.scala @@ -133,6 +133,9 @@ final case class TArray[G](element: Type[G])( final case class TPointer[G](element: Type[G])( implicit val o: Origin = DiagnosticOrigin ) extends Type[G] with TPointerImpl[G] +final case class TNonNullPointer[G](element: Type[G])( + implicit val o: Origin = DiagnosticOrigin +) extends Type[G] with TNonNullPointerImpl[G] final case class TType[G](t: Type[G])(implicit val o: Origin = DiagnosticOrigin) extends Type[G] with TTypeImpl[G] final case class TVar[G](ref: Ref[G, Variable[G]])( @@ -325,8 +328,9 @@ final case class LocalDecl[G](local: Variable[G])(implicit val o: Origin) extends NonExecutableStatement[G] with PurelySequentialStatement[G] with LocalDeclImpl[G] -final case class HeapLocalDecl[G](local: LocalHeapVariable[G])(implicit val o: Origin) - extends NonExecutableStatement[G] +final case class HeapLocalDecl[G](local: LocalHeapVariable[G])( + implicit val o: Origin +) extends NonExecutableStatement[G] with PurelySequentialStatement[G] with HeapLocalDeclImpl[G] final case class SpecIgnoreStart[G]()(implicit val o: Origin) @@ -1008,6 +1012,9 @@ final case class CoerceNullAnyClass[G]()(implicit val o: Origin) final case class CoerceNullPointer[G](pointerElementType: Type[G])( implicit val o: Origin ) extends Coercion[G] with CoerceNullPointerImpl[G] +final case class CoerceNonNullPointer[G](elementType: Type[G])( + implicit val o: Origin +) extends Coercion[G] with CoerceNonNullPointerImpl[G] final case class CoerceNullEnum[G](targetEnum: Ref[G, Enum[G]])( implicit val o: Origin ) extends Coercion[G] with CoerceNullEnumImpl[G] @@ -1367,8 +1374,9 @@ final case class ScopedExpr[G](declarations: Seq[Variable[G]], body: Expr[G])( final case class Local[G](ref: Ref[G, Variable[G]])(implicit val o: Origin) extends Expr[G] with LocalImpl[G] -final case class HeapLocal[G](ref: Ref[G, LocalHeapVariable[G]])(implicit val o: Origin) - extends Expr[G] with HeapLocalImpl[G] +final case class HeapLocal[G](ref: Ref[G, LocalHeapVariable[G]])( + implicit val o: Origin +) extends Expr[G] with HeapLocalImpl[G] final case class EnumUse[G]( enum: Ref[G, Enum[G]], @@ -1735,6 +1743,10 @@ final case class PointerLocation[G](pointer: Expr[G])( val blame: Blame[PointerLocationError] )(implicit val o: Origin) extends Location[G] with PointerLocationImpl[G] +final case class ByValueClassLocation[G](expr: Expr[G])( + val blame: Blame[PointerLocationError] +)(implicit val o: Origin) + extends Location[G] with ByValueClassLocationImpl[G] final case class PredicateLocation[G]( predicate: Ref[G, Predicate[G]], args: Seq[Expr[G]], @@ -1869,6 +1881,10 @@ final case class NewPointerArray[G](element: Type[G], size: Expr[G])( val blame: Blame[ArraySizeError] )(implicit val o: Origin) extends Expr[G] with NewPointerArrayImpl[G] +final case class NewNonNullPointerArray[G](element: Type[G], size: Expr[G])( + val blame: Blame[ArraySizeError] +)(implicit val o: Origin) + extends Expr[G] with NewNonNullPointerArrayImpl[G] final case class FreePointer[G](pointer: Expr[G])( val blame: Blame[PointerFreeError] )(implicit val o: Origin) @@ -2761,10 +2777,9 @@ final case class GpgpuAtomic[G]( extends CStatement[G] with GpgpuAtomicImpl[G] sealed trait CExpr[G] extends Expr[G] with CExprImpl[G] -final case class CLocal[G](name: String)( - val blame: Blame[DerefInsufficientPermission] -)(implicit val o: Origin) - extends CExpr[G] with CLocalImpl[G] { +final case class CLocal[G](name: String)(val blame: Blame[FrontendDerefError])( + implicit val o: Origin +) extends CExpr[G] with CLocalImpl[G] { var ref: Option[CNameTarget[G]] = None } final case class CInvocation[G]( diff --git a/src/col/vct/col/ast/expr/heap/alloc/NewNonNullPointerArrayImpl.scala b/src/col/vct/col/ast/expr/heap/alloc/NewNonNullPointerArrayImpl.scala new file mode 100644 index 0000000000..abc8896755 --- /dev/null +++ b/src/col/vct/col/ast/expr/heap/alloc/NewNonNullPointerArrayImpl.scala @@ -0,0 +1,14 @@ +package vct.col.ast.expr.heap.alloc + +import vct.col.ast.ops.NewNonNullPointerArrayOps +import vct.col.ast.{NewNonNullPointerArray, TNonNullPointer, Type} +import vct.col.print._ + +trait NewNonNullPointerArrayImpl[G] extends NewNonNullPointerArrayOps[G] { + this: NewNonNullPointerArray[G] => + override lazy val t: Type[G] = TNonNullPointer(element) + + override def precedence: Int = Precedence.POSTFIX + override def layout(implicit ctx: Ctx): Doc = + Text("new") <+> element <> "[" <> size <> "]" +} diff --git a/src/col/vct/col/ast/family/coercion/CoerceNonNullPointerImpl.scala b/src/col/vct/col/ast/family/coercion/CoerceNonNullPointerImpl.scala new file mode 100644 index 0000000000..2dd5617753 --- /dev/null +++ b/src/col/vct/col/ast/family/coercion/CoerceNonNullPointerImpl.scala @@ -0,0 +1,9 @@ +package vct.col.ast.family.coercion + +import vct.col.ast.ops.CoerceNonNullPointerOps +import vct.col.ast.{CoerceNonNullPointer, TPointer} + +trait CoerceNonNullPointerImpl[G] extends CoerceNonNullPointerOps[G] { + this: CoerceNonNullPointer[G] => + override def target: TPointer[G] = TPointer(elementType) +} diff --git a/src/col/vct/col/ast/family/location/ByValueClassLocationImpl.scala b/src/col/vct/col/ast/family/location/ByValueClassLocationImpl.scala new file mode 100644 index 0000000000..225d574c96 --- /dev/null +++ b/src/col/vct/col/ast/family/location/ByValueClassLocationImpl.scala @@ -0,0 +1,10 @@ +package vct.col.ast.family.location + +import vct.col.ast.ByValueClassLocation +import vct.col.ast.ops.ByValueClassLocationOps +import vct.col.print.{Ctx, Doc} + +trait ByValueClassLocationImpl[G] extends ByValueClassLocationOps[G] { + this: ByValueClassLocation[G] => + override def layout(implicit ctx: Ctx): Doc = expr.show +} diff --git a/src/col/vct/col/ast/type/TNonNullPointerImpl.scala b/src/col/vct/col/ast/type/TNonNullPointerImpl.scala new file mode 100644 index 0000000000..cd769efa94 --- /dev/null +++ b/src/col/vct/col/ast/type/TNonNullPointerImpl.scala @@ -0,0 +1,16 @@ +package vct.col.ast.`type` + +import vct.col.ast.TNonNullPointer +import vct.col.ast.ops.TNonNullPointerOps +import vct.col.print._ + +trait TNonNullPointerImpl[G] extends TNonNullPointerOps[G] { + this: TNonNullPointer[G] => + override def layoutSplitDeclarator(implicit ctx: Ctx): (Doc, Doc) = { + val (spec, decl) = element.layoutSplitDeclarator + (spec, decl <> "*") + } + + override def layout(implicit ctx: Ctx): Doc = + Group(Text("NonNull") <> open <> element <> close) +} diff --git a/src/col/vct/col/origin/Blame.scala b/src/col/vct/col/origin/Blame.scala index 174b70f334..50a1b2c6dc 100644 --- a/src/col/vct/col/origin/Blame.scala +++ b/src/col/vct/col/origin/Blame.scala @@ -159,7 +159,7 @@ case class AssignFieldFailed(node: SilverFieldAssign[_]) } case class CopyClassFailed(node: Node[_], clazz: ByValueClass[_], field: String) - extends AssignFailed with NodeVerificationFailure { + extends PointerDerefError with NodeVerificationFailure { override def code: String = "copyClassFailed" override def descInContext: String = s"Insufficient read permission for field '$field' to copy ${clazz.o @@ -172,7 +172,9 @@ case class CopyClassFailedBeforeCall( node: Node[_], clazz: ByValueClass[_], field: String, -) extends AssignFailed with InvocationFailure with NodeVerificationFailure { +) extends PointerDerefError + with InvocationFailure + with NodeVerificationFailure { override def code: String = "copyClassFailedBeforeCall" override def descInContext: String = s"Insufficient read permission for field '$field' to copy ${clazz.o @@ -1517,6 +1519,9 @@ object JavaArrayInitializerBlame "The explicit initialization of an array in Java should never generate an assignment that exceeds the bounds of the array" ) +object NonNullPointerNull + extends PanicBlame("A non-null pointer can never be null") + object UnsafeDontCare { case class Satisfiability(reason: String) extends UnsafeDontCare[NontrivialUnsatisfiable] diff --git a/src/col/vct/col/resolve/Resolve.scala b/src/col/vct/col/resolve/Resolve.scala index f88099f286..daa25bb41e 100644 --- a/src/col/vct/col/resolve/Resolve.scala +++ b/src/col/vct/col/resolve/Resolve.scala @@ -366,6 +366,7 @@ case object ResolveReferences extends LazyLogging { case CPPDeclarationStatement(decl) => Seq(decl) case JavaLocalDeclarationStatement(decl) => Seq(decl) case LocalDecl(v) => Seq(v) + case HeapLocalDecl(v) => Seq(v) case other => other.subnodes.flatMap(scanScope(ctx)) } diff --git a/src/col/vct/col/typerules/CoercingRewriter.scala b/src/col/vct/col/typerules/CoercingRewriter.scala index 26a81d7d8a..4c709d31cd 100644 --- a/src/col/vct/col/typerules/CoercingRewriter.scala +++ b/src/col/vct/col/typerules/CoercingRewriter.scala @@ -281,6 +281,7 @@ abstract class CoercingRewriter[Pre <: Generation]() case CoerceNullJavaClass(_) => e case CoerceNullAnyClass() => e case CoerceNullPointer(_) => e + case CoerceNonNullPointer(_) => e case CoerceFracZFrac() => e case CoerceZFracRat() => e case CoerceFloatRat(_) => e @@ -1566,6 +1567,8 @@ abstract class CoercingRewriter[Pre <: Generation]() NewArray(element, dims.map(int), moreDims, initialize)(na.blame) case na @ NewPointerArray(element, size) => NewPointerArray(element, size)(na.blame) + case na @ NewNonNullPointerArray(element, size) => + NewNonNullPointerArray(element, size)(na.blame) case NewObject(cls) => NewObject(cls) case NoPerm() => NoPerm() case Not(arg) => Not(bool(arg)) @@ -2686,6 +2689,7 @@ abstract class CoercingRewriter[Pre <: Generation]() ArrayLocation(array(arrayObj)._1, int(subscript))(a.blame) case p @ PointerLocation(pointerExp) => PointerLocation(pointer(pointerExp)._1)(p.blame) + case ByValueClassLocation(expr) => node case PredicateLocation(predicate, args) => PredicateLocation(predicate, coerceArgs(args, predicate.decl)) case InstancePredicateLocation(predicate, obj, args) => diff --git a/src/col/vct/col/typerules/CoercionUtils.scala b/src/col/vct/col/typerules/CoercionUtils.scala index deef9b73ca..a71e109e01 100644 --- a/src/col/vct/col/typerules/CoercionUtils.scala +++ b/src/col/vct/col/typerules/CoercionUtils.scala @@ -140,6 +140,9 @@ case object CoercionUtils { TPointer(element), ) => // if element == innerType => getAnyCoercion(element, innerType).getOrElse(return None) + case (TNonNullPointer(innerType), TPointer(element)) + if innerType == element => + CoerceNonNullPointer(innerType) case ( TPointer(element), CTPointer(innerType), @@ -430,6 +433,8 @@ case object CoercionUtils { case t: TPointer[G] => Some((CoerceIdentity(source), t)) case t: CTPointer[G] => Some((CoerceIdentity(source), TPointer(t.innerType))) + case t: TNonNullPointer[G] => + Some((CoerceIdentity(source), TPointer(t.element))) case t: CTArray[G] => Some((CoerceCArrayPointer(t.innerType), TPointer(t.innerType))) case t: CPPPrimitiveType[G] => chainCPPCoercion(t, getAnyPointerCoercion) diff --git a/src/col/vct/col/util/AstBuildHelpers.scala b/src/col/vct/col/util/AstBuildHelpers.scala index 8cbf2ba839..575d920219 100644 --- a/src/col/vct/col/util/AstBuildHelpers.scala +++ b/src/col/vct/col/util/AstBuildHelpers.scala @@ -106,7 +106,9 @@ object AstBuildHelpers { } implicit class LocalHeapVarBuildHelpers[G](left: LocalHeapVariable[G]) { - def get(implicit origin: Origin): HeapLocal[G] = HeapLocal(new DirectRef(left)) + def get(blame: Blame[PointerDerefError])( + implicit origin: Origin + ): DerefPointer[G] = DerefPointer(HeapLocal[G](new DirectRef(left)))(blame) } implicit class FieldBuildHelpers[G](left: SilverDeref[G]) { @@ -672,6 +674,13 @@ object AstBuildHelpers { )(implicit o: Origin): FunctionInvocation[G] = FunctionInvocation(ref, args, typeArgs, givenMap, yields)(blame) + def adtFunctionInvocation[G]( + ref: Ref[G, ADTFunction[G]], + typeArgs: Option[(Ref[G, AxiomaticDataType[G]], Seq[Type[G]])] = None, + args: Seq[Expr[G]] = Nil, + )(implicit o: Origin): ADTFunctionInvocation[G] = + ADTFunctionInvocation(typeArgs, ref, args) + def methodInvocation[G]( blame: Blame[InstanceInvocationFailure], obj: Expr[G], @@ -768,10 +777,6 @@ object AstBuildHelpers { implicit o: Origin ): Assign[G] = Assign(local, value)(AssignLocalOk) - def assignHeapLocal[G](local: HeapLocal[G], value: Expr[G])( - implicit o: Origin - ): Assign[G] = Assign(local, value)(AssignLocalOk) - def assignField[G]( obj: Expr[G], field: Ref[G, InstanceField[G]], diff --git a/src/main/vct/main/stages/Transformation.scala b/src/main/vct/main/stages/Transformation.scala index 15c61d3b8d..f75ed061ee 100644 --- a/src/main/vct/main/stages/Transformation.scala +++ b/src/main/vct/main/stages/Transformation.scala @@ -29,13 +29,15 @@ import vct.result.VerificationError.SystemError import vct.rewrite.adt.ImportSetCompat import vct.rewrite.{ EncodeAutoValue, + PrepareByValueClass, EncodeRange, EncodeResourceValues, ExplicitResourceValues, HeapVariableToRef, + LowerLocalHeapVariables, MonomorphizeClass, SmtlibToProverTypes, - EncodeByValueClass, + VariableToPointer, } import vct.rewrite.lang.ReplaceSYCLTypes import vct.rewrite.veymont.{ @@ -326,7 +328,8 @@ case class SilverTransformation( EncodeString, // Encode spec string as seq EncodeChar, CollectLocalDeclarations, // all decls in Scope - EncodeByValueClass, +// EncodeByValueClass, + VariableToPointer, // should happen before ParBlockEncoder so it can distinguish between variables which can and can't altered in a parallel block DesugarPermissionOperators, // no PointsTo, \pointer, etc. ReadToValue, // resolve wildcard into fractional permission TrivialAddrOf, @@ -335,6 +338,7 @@ case class SilverTransformation( QuantifySubscriptAny, // no arr[*] IterationContractToParBlock, PropagateContextEverywhere, // inline context_everywhere into loop invariants + PrepareByValueClass, EncodeArrayValues, // maybe don't target shift lemmas on generated function for \values GivenYieldsToArgs, CheckProcessAlgebra, @@ -384,6 +388,7 @@ case class SilverTransformation( // No more classes ClassToRef, HeapVariableToRef, + LowerLocalHeapVariables, CheckContractSatisfiability.withArg(checkSat), DesugarCollectionOperators, EncodeNdIndex, diff --git a/src/rewrite/vct/rewrite/ClassToRef.scala b/src/rewrite/vct/rewrite/ClassToRef.scala index b73c52606a..a3c29fbe64 100644 --- a/src/rewrite/vct/rewrite/ClassToRef.scala +++ b/src/rewrite/vct/rewrite/ClassToRef.scala @@ -22,6 +22,10 @@ case object ClassToRef extends RewriterBuilder { private def InstanceOfOrigin: Origin = Origin(Seq(PreferredName(Seq("subtype")), LabelContext("classToRef"))) + private val PointerCreationOrigin: Origin = Origin( + Seq(LabelContext("classToRef, pointer creation method")) + ) + case class InstanceNullPreconditionFailed( inner: Blame[InstanceNull], inv: InvokingNode[_], @@ -38,11 +42,19 @@ case class ClassToRef[Pre <: Generation]() extends Rewriter[Pre] { private def This: Origin = Origin(Seq(PreferredName(Seq("this")), LabelContext("classToRef"))) - val fieldSucc: SuccessionMap[Field[Pre], SilverField[Post]] = SuccessionMap() + val byRefFieldSucc: SuccessionMap[Field[Pre], SilverField[Post]] = + SuccessionMap() + val byValFieldSucc: SuccessionMap[Field[Pre], ADTFunction[Post]] = + SuccessionMap() + val byValClassSucc + : SuccessionMap[ByValueClass[Pre], AxiomaticDataType[Post]] = + SuccessionMap() val methodSucc: SuccessionMap[InstanceMethod[Pre], Procedure[Post]] = SuccessionMap() val consSucc: SuccessionMap[Constructor[Pre], Procedure[Post]] = SuccessionMap() + val byValConsSucc: SuccessionMap[ByValueClass[Pre], ADTFunction[Post]] = + SuccessionMap() val functionSucc: SuccessionMap[InstanceFunction[Pre], Function[Post]] = SuccessionMap() val predicateSucc: SuccessionMap[InstancePredicate[Pre], Predicate[Post]] = @@ -53,6 +65,26 @@ case class ClassToRef[Pre <: Generation]() extends Rewriter[Pre] { var typeNumberStore: mutable.Map[Class[Pre], Int] = mutable.Map() val typeOf: SuccessionMap[Unit, Function[Post]] = SuccessionMap() val instanceOf: SuccessionMap[Unit, Function[Post]] = SuccessionMap() + private val pointerCreationMethods + : SuccessionMap[Type[Pre], Procedure[Post]] = SuccessionMap() + + def makePointerCreationMethod(t: Type[Post]): Procedure[Post] = { + implicit val o: Origin = PointerCreationOrigin + + val result = new Variable[Post](TNonNullPointer(t)) + globalDeclarations.declare(procedure[Post]( + blame = AbstractApplicable, + contractBlame = TrueSatisfiable, + returnType = TVoid(), + outArgs = Seq(result), + ensures = UnitAccountedPredicate( + (PointerBlockLength(result.get)(FramedPtrBlockLength) === const(1)) &* + (PointerBlockOffset(result.get)(FramedPtrOffset) === const(0)) &* + Perm(PointerLocation(result.get)(FramedPtrOffset), WritePerm()) + ), + decreases = Some(DecreasesClauseNoRecursion[Post]()), + )) + } def typeNumber(cls: Class[Pre]): Int = typeNumberStore.getOrElseUpdate(cls, typeNumberStore.size + 1) @@ -143,7 +175,6 @@ case class ClassToRef[Pre <: Generation]() extends Rewriter[Pre] { ) typeNumber(cls) - cls.drop() cls.decls.foreach { case function: InstanceFunction[Pre] => implicit val o: Origin = function.o @@ -278,33 +309,182 @@ case class ClassToRef[Pre <: Generation]() extends Rewriter[Pre] { ) } case field: Field[Pre] => - fieldSucc(field) = new SilverField(dispatch(field.t))(field.o) - globalDeclarations.declare(fieldSucc(field)) + if (cls.isInstanceOf[ByReferenceClass[Pre]]) { + byRefFieldSucc(field) = + new SilverField(dispatch(field.t))(field.o) + globalDeclarations.declare(byRefFieldSucc(field)) + } case _ => throw ExtraNode } + cls match { + case cls: ByValueClass[Pre] => + implicit val o: Origin = cls.o + val axiomType = TAxiomatic[Post](byValClassSucc.ref(cls), Nil) + val (fieldFunctions, fieldTypes) = + cls.decls.collect { case field: Field[Pre] => + val newT = dispatch(field.t) + byValFieldSucc(field) = + new ADTFunction[Post]( + Seq(new Variable(axiomType)(field.o)), + newT, + )(field.o) + (byValFieldSucc(field), newT) + }.unzip + val constructor = + new ADTFunction[Post](fieldTypes.map(new Variable(_)), axiomType)( + cls.o + ) + val destructorAxiom = + new ADTAxiom[Post](foralls( + fieldTypes, + body = + variables => { + foldAnd(variables.zip(fieldFunctions).map { case (v, f) => + adtFunctionInvocation[Post]( + f.ref, + args = Seq(adtFunctionInvocation[Post]( + constructor.ref, + None, + args = variables, + )), + ) === v + }) + }, + triggers = + variables => { + fieldFunctions.map { f => + Seq(adtFunctionInvocation[Post]( + f.ref, + args = Seq(adtFunctionInvocation[Post]( + constructor.ref, + None, + args = variables, + )), + )) + } + }, + )) + val nonNullAxiom = + new ADTAxiom[Post](forall( + axiomType, + body = + v => { + foldAnd(fieldFunctions.map { f => + adtFunctionInvocation[Post]( + f.ref, + None, + args = Seq(v), + ) !== Null() + }) + }, + )) + // TODO: need Non null pointer.... + val injectivityAxiom = + new ADTAxiom[Post](foralls( + Seq(axiomType, axiomType), + body = { case Seq(a0, a1) => + (a0 !== a1) ==> foldAnd(fieldFunctions.map { f => + DerefPointer( + adtFunctionInvocation[Post](f.ref, args = Seq(a0)) + )(NonNullPointerNull)( + o.withContent(TypeName("helloWorld")) + ) !== DerefPointer( + adtFunctionInvocation[Post](f.ref, args = Seq(a1)) + )(NonNullPointerNull)(o.withContent(TypeName("helloWorld"))) + }) + }, + triggers = { case Seq(a0, a1) => + fieldFunctions.map { f => + Seq( + DerefPointer( + adtFunctionInvocation[Post](f.ref, None, args = Seq(a0)) + )(NonNullPointerNull)(o.withContent(TypeName( + "helloWorld" + ))), + DerefPointer( + adtFunctionInvocation[Post](f.ref, None, args = Seq(a1)) + )(NonNullPointerNull)(o.withContent(TypeName( + "helloWorld" + ))), + ) + } + }, + )) + byValConsSucc(cls) = constructor + byValClassSucc(cls) = + new AxiomaticDataType[Post]( + Seq( + constructor, + destructorAxiom, +// nonNullAxiom, + injectivityAxiom, + ) ++ fieldFunctions, + Nil, + ) + globalDeclarations.succeed(cls, byValClassSucc(cls)) + case _ => cls.drop() + } case decl => rewriteDefault(decl) } def instantiate(cls: Class[Pre], target: Ref[Post, Variable[Post]])( implicit o: Origin ): Statement[Post] = { - Block(Seq( - SilverNewRef[Post]( - target, - cls.decls.collect { case field: InstanceField[Pre] => - fieldSucc.ref(field) - }, - ), - Inhale( - FunctionInvocation[Post]( - typeOf.ref(()), - Seq(Local(target)), - Nil, - Nil, - Nil, - )(PanicBlame("typeOf requires nothing.")) === const(typeNumber(cls)) - ), - )) + cls match { + case cls: ByReferenceClass[Pre] => + Block(Seq( + SilverNewRef[Post]( + target, + cls.decls.collect { case field: InstanceField[Pre] => + byRefFieldSucc.ref(field) + }, + ), + Inhale( + FunctionInvocation[Post]( + typeOf.ref(()), + Seq(Local(target)), + Nil, + Nil, + Nil, + )(PanicBlame("typeOf requires nothing.")) === const(typeNumber(cls)) + ), + )) + case cls: ByValueClass[Pre] => + val (assigns, vars) = + cls.decls.collect { case field: InstanceField[Pre] => + val element = field.t.asPointer.get.element + val newE = dispatch(element) + val v = new Variable[Post](TNonNullPointer(newE)) + ( + InvokeProcedure[Post]( + pointerCreationMethods + .getOrElseUpdate(element, makePointerCreationMethod(newE)) + .ref, + Nil, + Seq(v.get), + Nil, + Nil, + Nil, + )(TrueSatisfiable), + v, + ) + }.unzip + Scope( + vars, + Block( + assigns ++ Seq( + Assign( + Local(target), + adtFunctionInvocation[Post]( + byValConsSucc.ref(cls), + args = vars.map(_.get), + ), + )(AssignLocalOk) + // TODO: Add back typeOf here (but use a separate definition for the adt) + ) + ), + ) + } } override def dispatch(stat: Statement[Pre]): Statement[Post] = @@ -423,9 +603,17 @@ case class ClassToRef[Pre <: Generation]() extends Rewriter[Pre] { ))(inv.o) case ThisObject(_) => diz.top case deref @ Deref(obj, Ref(field)) => - SilverDeref[Post](dispatch(obj), fieldSucc.ref(field))(deref.blame)( - deref.o - ) + obj.t match { + case _: TByReferenceClass[Pre] => + SilverDeref[Post](dispatch(obj), byRefFieldSucc.ref(field))( + deref.blame + )(deref.o) + case _: TByValueClass[Pre] => + adtFunctionInvocation[Post]( + byValFieldSucc.ref(field), + args = Seq(dispatch(obj)), + )(deref.o) + } case TypeValue(t) => t match { case t: TClass[Pre] if t.typeArgs.isEmpty => @@ -493,7 +681,12 @@ case class ClassToRef[Pre <: Generation]() extends Rewriter[Pre] { override def dispatch(t: Type[Pre]): Type[Post] = t match { - case _: TClass[Pre] => TRef() + case _: TByReferenceClass[Pre] => TRef() + case t: TByValueClass[Pre] => + TAxiomatic( + byValClassSucc.ref(t.cls.decl.asInstanceOf[ByValueClass[Pre]]), + Nil, + ) case TAnyClass() => TRef() case t => rewriteDefault(t) } @@ -505,10 +698,21 @@ case class ClassToRef[Pre <: Generation]() extends Rewriter[Pre] { predicateSucc.ref(predicate.decl), dispatch(obj) +: args.map(dispatch), )(loc.o) - case FieldLocation(obj, field) => - SilverFieldLocation[Post](dispatch(obj), fieldSucc.ref(field.decl))( - loc.o - ) + case FieldLocation(obj, Ref(field)) => + obj.t match { + case _: TByReferenceClass[Pre] => + SilverFieldLocation[Post](dispatch(obj), byRefFieldSucc.ref(field))( + loc.o + ) + case _: TByValueClass[Pre] => + PointerLocation[Post]( + adtFunctionInvocation[Post]( + byValFieldSucc.ref(field), + None, + args = Seq(dispatch(obj)), + )(loc.o) + )(NonNullPointerNull)(loc.o) + } case default => rewriteDefault(default) } } diff --git a/src/rewrite/vct/rewrite/DisambiguateLocation.scala b/src/rewrite/vct/rewrite/DisambiguateLocation.scala index 8209136550..e013f80915 100644 --- a/src/rewrite/vct/rewrite/DisambiguateLocation.scala +++ b/src/rewrite/vct/rewrite/DisambiguateLocation.scala @@ -45,6 +45,8 @@ case class DisambiguateLocation[Pre <: Generation]() extends Rewriter[Pre] { ArrayLocation(dispatch(arr), dispatch(index))(expr.blame) case expr if expr.t.asPointer.isDefined => PointerLocation(dispatch(expr))(blame) + case expr if expr.t.isInstanceOf[TByValueClass[Pre]] => + ByValueClassLocation(dispatch(expr))(blame) case PredicateApply(ref, args, WritePerm()) => PredicateLocation(succ(ref.decl), (args.map(dispatch))) case InstancePredicateApply(obj, ref, args, WritePerm()) => @@ -53,7 +55,6 @@ case class DisambiguateLocation[Pre <: Generation]() extends Rewriter[Pre] { dispatch(obj), args.map(dispatch), ) - case InlinePattern(inner, pattern, group) => InLinePatternLocation( exprToLoc(inner, blame), diff --git a/src/rewrite/vct/rewrite/EncodeArrayValues.scala b/src/rewrite/vct/rewrite/EncodeArrayValues.scala index 7c1732761f..a134a63a58 100644 --- a/src/rewrite/vct/rewrite/EncodeArrayValues.scala +++ b/src/rewrite/vct/rewrite/EncodeArrayValues.scala @@ -60,13 +60,15 @@ case object EncodeArrayValues extends RewriterBuilder { } } - case class PointerArrayCreationFailed(arr: NewPointerArray[_]) - extends Blame[InvocationFailure] { + case class PointerArrayCreationFailed( + arr: Expr[_], + blame: Blame[ArraySizeError], + ) extends Blame[InvocationFailure] { override def blame(error: InvocationFailure): Unit = error match { - case PreconditionFailed(_, _, _) => arr.blame.blame(ArraySize(arr)) + case PreconditionFailed(_, _, _) => blame.blame(ArraySize(arr)) case ContextEverywhereFailedInPre(_, _) => - arr.blame.blame(ArraySize(arr)) // Unnecessary? + blame.blame(ArraySize(arr)) // Unnecessary? case other => throw Unreachable(s"Invalid invocation failure: $other") } } @@ -106,6 +108,8 @@ case class EncodeArrayValues[Pre <: Generation]() extends Rewriter[Pre] { val pointerArrayCreationMethods: mutable.Map[Type[Pre], Procedure[Post]] = mutable.Map() + val nonNullPointerArrayCreationMethods + : mutable.Map[Type[Pre], Procedure[Post]] = mutable.Map() val freeMethods: mutable.Map[Type[ Post @@ -189,7 +193,7 @@ case class EncodeArrayValues[Pre <: Generation]() extends Rewriter[Pre] { // If structure contains structs, the permission for those fields need to be released as well val permFields = t match { - case t: TClass[Post] => unwrapStructPerm(access, t, o, makeStruct) +// case t: TClass[Post] => unwrapStructPerm(access, t, o, makeStruct) case _ => Seq() } requiresT = @@ -421,6 +425,7 @@ case class EncodeArrayValues[Pre <: Generation]() extends Rewriter[Pre] { ) // We do not allow this notation for recursive structs implicit val o: Origin = origin + // TODO: Instead of doing complicated stuff here just generate a Perm(struct.field, write) and rely on EncodyByValueClass to deal with it :) val fields = structType match { case t: TClass[Post] => @@ -431,7 +436,10 @@ case class EncodeArrayValues[Pre <: Generation]() extends Rewriter[Pre] { } val newFieldPerms = fields.map(member => { val loc = - (i: Variable[Post]) => Deref[Post](struct(i), member.ref)(DerefPerm) + (i: Variable[Post]) => + DerefPointer(Deref[Post](struct(i), member.ref)(DerefPerm))( + NonNullPointerNull + ) var anns: Seq[(Expr[Post], Expr[Pre] => PointerFreeError)] = Seq(( makeStruct.makePerm( i => FieldLocation[Post](struct(i), member.ref), @@ -444,7 +452,7 @@ case class EncodeArrayValues[Pre <: Generation]() extends Rewriter[Pre] { ), )) anns = - if (typeIsRef(member.t)) + if (typeIsRef(member.t.asPointer.get.element)) anns :+ ( makeStruct.makeUnique(loc), @@ -452,7 +460,7 @@ case class EncodeArrayValues[Pre <: Generation]() extends Rewriter[Pre] { ) else anns - member.t match { + member.t.asPointer.get.element match { case newStruct: TClass[Post] => // We recurse, since a field is another struct anns ++ unwrapStructPerm( @@ -504,7 +512,10 @@ case class EncodeArrayValues[Pre <: Generation]() extends Rewriter[Pre] { case _ => false } - def makePointerCreationMethodFor(elementType: Type[Pre]) = { + def makePointerCreationMethodFor( + elementType: Type[Pre], + nullable: Boolean, + ) = { implicit val o: Origin = arrayCreationOrigin // ar != null // ar.length == dim0 @@ -529,9 +540,11 @@ case class EncodeArrayValues[Pre <: Generation]() extends Rewriter[Pre] { Seq(access(i), access(j)), ) - var ensures = (result !== Null()) &* + var ensures = (PointerBlockLength(result)(FramedPtrBlockLength) === sizeArg.get) &* - (PointerBlockOffset(result)(FramedPtrBlockOffset) === zero) + (PointerBlockOffset(result)(FramedPtrBlockOffset) === zero) + + if (nullable) { ensures = (result !== Null()) &* ensures } // Pointer location needs pointer add, not pointer subscript ensures = ensures &* makeStruct.makePerm( @@ -561,7 +574,9 @@ case class EncodeArrayValues[Pre <: Generation]() extends Rewriter[Pre] { procedure( blame = AbstractApplicable, contractBlame = TrueSatisfiable, - returnType = TPointer(dispatch(elementType)), + returnType = + if (nullable) { TPointer(dispatch(elementType)) } + else { TNonNullPointer(dispatch(elementType)) }, args = Seq(sizeArg), requires = UnitAccountedPredicate(requires), ensures = UnitAccountedPredicate(ensures), @@ -597,8 +612,23 @@ case class EncodeArrayValues[Pre <: Generation]() extends Rewriter[Pre] { Nil, )(ArrayCreationFailed(newArr)) case newPointerArr @ NewPointerArray(element, size) => - val method = pointerArrayCreationMethods - .getOrElseUpdate(element, makePointerCreationMethodFor(element)) + val method = pointerArrayCreationMethods.getOrElseUpdate( + element, + makePointerCreationMethodFor(element, nullable = true), + ) + ProcedureInvocation[Post]( + method.ref, + Seq(dispatch(size)), + Nil, + Nil, + Nil, + Nil, + )(PointerArrayCreationFailed(newPointerArr, newPointerArr.blame)) + case newPointerArr @ NewNonNullPointerArray(element, size) => + val method = nonNullPointerArrayCreationMethods.getOrElseUpdate( + element, + makePointerCreationMethodFor(element, nullable = false), + ) ProcedureInvocation[Post]( method.ref, Seq(dispatch(size)), @@ -606,7 +636,7 @@ case class EncodeArrayValues[Pre <: Generation]() extends Rewriter[Pre] { Nil, Nil, Nil, - )(PointerArrayCreationFailed(newPointerArr)) + )(PointerArrayCreationFailed(newPointerArr, newPointerArr.blame)) case free @ FreePointer(xs) => val newXs = dispatch(xs) val TPointer(t) = newXs.t diff --git a/src/rewrite/vct/rewrite/EncodeByValueClass.scala b/src/rewrite/vct/rewrite/EncodeByValueClass.scala deleted file mode 100644 index 3054e2572c..0000000000 --- a/src/rewrite/vct/rewrite/EncodeByValueClass.scala +++ /dev/null @@ -1,249 +0,0 @@ -package vct.rewrite - -import hre.util.ScopedStack -import vct.col.ast._ -import vct.col.origin._ -import vct.col.ref.Ref -import vct.col.resolve.ctx.Referrable -import vct.col.rewrite.{Generation, Rewriter, RewriterBuilder} -import vct.col.util.AstBuildHelpers._ -import vct.result.VerificationError.UserError - -case object EncodeByValueClass extends RewriterBuilder { - override def key: String = "encodeByValueClass" - - override def desc: String = - "Initialise ByValueClasses when they are declared and copy them whenever they're read" - - private case class ClassCopyInAssignmentFailed( - blame: Blame[AssignFailed], - assign: Node[_], - clazz: ByValueClass[_], - field: InstanceField[_], - ) extends Blame[InsufficientPermission] { - override def blame(error: InsufficientPermission): Unit = { - if (blame.isInstanceOf[PanicBlame]) { - assign.o - .blame(CopyClassFailed(assign, clazz, Referrable.originName(field))) - } else { - blame - .blame(CopyClassFailed(assign, clazz, Referrable.originName(field))) - } - } - } - - private case class ClassCopyInCallFailed( - blame: Blame[InvocationFailure], - inv: Invocation[_], - clazz: ByValueClass[_], - field: InstanceField[_], - ) extends Blame[InsufficientPermission] { - override def blame(error: InsufficientPermission): Unit = { - blame.blame( - CopyClassFailedBeforeCall(inv, clazz, Referrable.originName(field)) - ) - } - } - - case class UnsupportedStructPerm(o: Origin) extends UserError { - override def code: String = "unsupportedStructPerm" - override def text: String = - o.messageInContext( - "Shorthand for Permissions for structs not possible, since the struct has a cyclic reference" - ) - } - - private sealed class CopyContext - - private case class InCall(invocation: Invocation[_]) extends CopyContext - - private case class InAssignmentExpression(assignment: AssignExpression[_]) - extends CopyContext - - private case class InAssignmentStatement(assignment: Assign[_]) - extends CopyContext -} - -case class EncodeByValueClass[Pre <: Generation]() extends Rewriter[Pre] { - - import EncodeByValueClass._ - - private val inAssignment: ScopedStack[Unit] = ScopedStack() - private val copyContext: ScopedStack[CopyContext] = ScopedStack() - - override def dispatch(node: Statement[Pre]): Statement[Post] = - node match { - case s: Scope[Pre] => - cPPLocalDeclarations.scope { - cLocalDeclarations.scope { - variables.scope { - localHeapVariables.scope { - val locals = variables.dispatch(s.locals) - Scope( - locals, - Block(locals.collect { - case v: Variable[Post] - if v.t.isInstanceOf[TByValueClass[Post]] => - Assign( - v.get(v.o), - NewObject(v.t.asInstanceOf[TByValueClass[Post]].cls)(v.o), - )(PanicBlame( - "Instantiating a ByValueClass should always succeed" - ))(v.o) - } ++ Seq(s.body.rewriteDefault()))(node.o), - )(node.o) - } - } - } - } - case assign: Assign[Pre] => { - val target = inAssignment.having(()) { assign.target.rewriteDefault() } - copyContext.having(InAssignmentStatement(assign)) { - assign.rewrite(target = target) - } - } - case _ => node.rewriteDefault() - } - - private def copyClassValue( - obj: Expr[Post], - t: TByValueClass[Pre], - blame: InstanceField[Pre] => Blame[InsufficientPermission], - ): Expr[Post] = { - implicit val o: Origin = obj.o - val v = new Variable[Post](dispatch(t)) - val children = t.cls.decl.decls.collect { case f: InstanceField[Pre] => - f.t match { - case inner: TByValueClass[Pre] => - Assign[Post]( - Deref[Post](v.get, succ(f))(DerefAssignTarget), - copyClassValue(Deref[Post](obj, succ(f))(blame(f)), inner, blame), - )(AssignLocalOk) - case _ => - Assign[Post]( - Deref[Post](v.get, succ(f))(DerefAssignTarget), - Deref[Post](obj, succ(f))(blame(f)), - )(AssignLocalOk) - - } - } - ScopedExpr( - Seq(v), - Then( - PreAssignExpression(v.get, NewObject[Post](succ(t.cls.decl)))( - AssignLocalOk - ), - Block(children), - ), - ) - } - - // def unwrapClassPerm( - // struct: Expr[Post], - // perm: Expr[Pre], - // structType: TByValueClass[Pre], - // origin: Origin, - // visited: Seq[TByValueClass[Pre]] = Seq(), - // ): Expr[Post] = { - // if (visited.contains(structType)) - // throw UnsupportedStructPerm( - // origin - // ) // We do not allow this notation for recursive structs - // implicit val o: Origin = origin - // val blame = PanicBlame("Field permission is framed") - // val Seq(CStructDeclaration(_, fields)) = structType.ref.decl.decl.specs - // val newPerm = dispatch(perm) - // val AmbiguousLocation(newExpr) = struct - // val newFieldPerms = fields.map(member => { - // val loc = - // AmbiguousLocation( - // Deref[Post]( - // newExpr, - // cStructFieldsSuccessor.ref((structType.ref.decl, member)), - // )(blame) - // )(struct.blame) - // member.specs.collectFirst { - // case CSpecificationType(newStruct: CTStruct[Pre]) => - // // We recurse, since a field is another struct - // Perm(loc, newPerm) &* unwrapStructPerm( - // loc, - // perm, - // newStruct, - // origin, - // structType +: visited, - // ) - // }.getOrElse(Perm(loc, newPerm)) - // }) - - // foldStar(newFieldPerms) - // } - override def dispatch(node: Expr[Pre]): Expr[Post] = - if (inAssignment.nonEmpty) - node.rewriteDefault() - else - node match { - case Perm(loc, p) => node.rewriteDefault() - case assign: PreAssignExpression[Pre] => - val target = - inAssignment.having(()) { assign.target.rewriteDefault() } - copyContext.having(InAssignmentExpression(assign)) { - assign.rewrite(target = target) - } - case invocation: Invocation[Pre] => { - copyContext.having(InCall(invocation)) { invocation.rewriteDefault() } - } - case Local(Ref(v)) if v.t.isInstanceOf[TByValueClass[Pre]] => - if (copyContext.isEmpty) { - return node.rewriteDefault() - } // If we are in other kinds of expressions like if statements - val t = v.t.asInstanceOf[TByValueClass[Pre]] - val clazz = t.cls.decl.asInstanceOf[ByValueClass[Pre]] - - copyContext.top match { - case InCall(invocation) => - copyClassValue( - node.rewriteDefault(), - t, - f => - ClassCopyInCallFailed(invocation.blame, invocation, clazz, f), - ) - case InAssignmentExpression(assignment: PreAssignExpression[_]) => - copyClassValue( - node.rewriteDefault(), - t, - f => - ClassCopyInAssignmentFailed( - assignment.blame, - assignment, - clazz, - f, - ), - ) - case InAssignmentExpression(assignment: PostAssignExpression[_]) => - copyClassValue( - node.rewriteDefault(), - t, - f => - ClassCopyInAssignmentFailed( - assignment.blame, - assignment, - clazz, - f, - ), - ) - case InAssignmentStatement(assignment) => - copyClassValue( - node.rewriteDefault(), - t, - f => - ClassCopyInAssignmentFailed( - assignment.blame, - assignment, - clazz, - f, - ), - ) - } - case _ => node.rewriteDefault() - } -} diff --git a/src/rewrite/vct/rewrite/LowerLocalHeapVariables.scala b/src/rewrite/vct/rewrite/LowerLocalHeapVariables.scala new file mode 100644 index 0000000000..9e3fdad2a7 --- /dev/null +++ b/src/rewrite/vct/rewrite/LowerLocalHeapVariables.scala @@ -0,0 +1,111 @@ +package vct.rewrite + +import vct.col.rewrite.{Generation, Rewriter, RewriterBuilder} +import vct.col.ast.{Variable, _} +import vct.col.origin.{AssignLocalOk, LabelContext, Origin, PanicBlame} +import vct.col.util.AstBuildHelpers._ +import vct.col.ref.Ref +import vct.col.util.{CurrentRewriteProgramContext, SuccessionMap} +import vct.result.VerificationError + +case object LowerLocalHeapVariables extends RewriterBuilder { + override def key: String = "lowerLocalHeapVariables" + + override def desc: String = + "Lower LocalHeapVariables to Variables if their address is never taken" + + private val pointerCreationOrigin: Origin = Origin( + Seq(LabelContext("pointer creation method")) + ) +} + +case class LowerLocalHeapVariables[Pre <: Generation]() extends Rewriter[Pre] { + import LowerLocalHeapVariables._ + + private val stripped: SuccessionMap[LocalHeapVariable[Pre], Variable[Post]] = + SuccessionMap() + private val lowered: SuccessionMap[LocalHeapVariable[Pre], Variable[Post]] = + SuccessionMap() +// private val pointerCreationMethods: SuccessionMap[Type[Pre], Procedure[Post]] = SuccessionMap() +// +// def makePointerCreationMethod(t: Type[Pre]): Procedure[Post] = { +// implicit val o: Origin = pointerCreationOrigin +// +// val proc = globalDeclarations.declare(withResult((result: Result[Post]) => { +// +// })) +// } + + override def dispatch(program: Program[Pre]): Program[Post] = { + val dereferencedHeapLocals = program.collect { + case DerefPointer(hl @ HeapLocal(_)) => System.identityHashCode(hl) + } + val nakedHeapLocals = program.collect { + case hl @ HeapLocal(Ref(v)) + if !dereferencedHeapLocals.contains(System.identityHashCode(hl)) => + v + } + VerificationError.withContext(CurrentRewriteProgramContext(program)) { + localHeapVariables.scope { + variables.scope { + enumConstants.scope { + modelDeclarations.scope { + aDTDeclarations.scope { + classDeclarations.scope { + globalDeclarations.scope { + program.collect { + case HeapLocal(Ref(v)) if !nakedHeapLocals.contains(v) => + v + }.foreach(v => + stripped(v) = + new Variable[Post](dispatch(v.t.asPointer.get.element))( + v.o + ) + ) + Program(globalDeclarations.dispatch(program.declarations))( + dispatch(program.blame) + )(program.o) + } + } + } + } + } + } + } + } + } + + override def dispatch(node: Statement[Pre]): Statement[Post] = { + implicit val o: Origin = node.o + node match { + // Same logic as CollectLocalDeclarations + case Scope(vars, impl) => + val (newVars, newImpl) = variables.collect { + vars.foreach(dispatch) + dispatch(impl) + } + Scope(newVars, newImpl) + case HeapLocalDecl(v) => + if (stripped.contains(v)) { variables.declare(stripped(v)) } + else { + lowered(v) = new Variable[Post](dispatch(v.t))(v.o) + variables.declare(lowered(v)) + } + Block(Nil) + case _ => node.rewriteDefault() + } + } + + override def dispatch(node: Expr[Pre]): Expr[Post] = { + implicit val o: Origin = node.o + node match { + case DerefPointer(HeapLocal(Ref(v))) if stripped.contains(v) => + stripped(v).get + case HeapLocal(Ref(v)) if lowered.contains(v) => { + // lowered.contains(v) should always be true since all stripped HeapLocals would be caught by DerefPointer(HeapLocal(Ref(v))) + Local(lowered.ref(v)) + } + case _ => node.rewriteDefault() + } + } +} diff --git a/src/rewrite/vct/rewrite/PrepareByValueClass.scala b/src/rewrite/vct/rewrite/PrepareByValueClass.scala new file mode 100644 index 0000000000..8067a5ec3b --- /dev/null +++ b/src/rewrite/vct/rewrite/PrepareByValueClass.scala @@ -0,0 +1,279 @@ +package vct.rewrite + +import hre.util.ScopedStack +import vct.col.ast._ +import vct.col.origin._ +import vct.col.ref.Ref +import vct.col.resolve.ctx.Referrable +import vct.col.rewrite.{Generation, Rewriter, RewriterBuilder} +import vct.col.util.AstBuildHelpers._ +import vct.result.VerificationError.{Unreachable, UserError} + +// TODO: Think of a better name +case object PrepareByValueClass extends RewriterBuilder { + override def key: String = "prepareByValueClass" + + override def desc: String = + "Initialise ByValueClasses when they are declared and copy them whenever they're read" + + private case class ClassCopyInAssignmentFailed( + blame: Blame[PointerDerefError], + assign: Node[_], + clazz: ByValueClass[_], + field: InstanceField[_], + ) extends Blame[InsufficientPermission] { + override def blame(error: InsufficientPermission): Unit = { + if (blame.isInstanceOf[PanicBlame]) { + assign.o + .blame(CopyClassFailed(assign, clazz, Referrable.originName(field))) + } else { + blame + .blame(CopyClassFailed(assign, clazz, Referrable.originName(field))) + } + } + } + + private case class ClassCopyInCallFailed( + blame: Blame[PointerDerefError], + inv: Invocation[_], + clazz: ByValueClass[_], + field: InstanceField[_], + ) extends Blame[InsufficientPermission] { + override def blame(error: InsufficientPermission): Unit = { + blame.blame( + CopyClassFailedBeforeCall(inv, clazz, Referrable.originName(field)) + ) + } + } + + case class UnsupportedStructPerm(o: Origin) extends UserError { + override def code: String = "unsupportedStructPerm" + override def text: String = + o.messageInContext( + "Shorthand for Permissions for structs not possible, since the struct has a cyclic reference" + ) + } + + private sealed class CopyContext + + private case class InCall(invocation: Invocation[_]) extends CopyContext + + private case class InAssignmentExpression(assignment: AssignExpression[_]) + extends CopyContext + + private case class InAssignmentStatement(assignment: Assign[_]) + extends CopyContext + + case class PointerLocationDerefBlame(blame: Blame[PointerLocationError]) + extends Blame[PointerDerefError] { + override def blame(error: PointerDerefError): Unit = { + error match { + case error: PointerLocationError => blame.blame(error) + case _ => + Unreachable( + "Blame of the respective pointer operation should be used not of DerefPointer" + ) + } + } + } +} + +case class PrepareByValueClass[Pre <: Generation]() extends Rewriter[Pre] { + + import PrepareByValueClass._ + + private val inAssignment: ScopedStack[Unit] = ScopedStack() + private val copyContext: ScopedStack[CopyContext] = ScopedStack() + + override def dispatch(node: Statement[Pre]): Statement[Post] = { + implicit val o: Origin = node.o + node match { + case HeapLocalDecl(local) + if local.t.asPointer.get.element.isInstanceOf[TByValueClass[Pre]] => { + val newLocal = localHeapVariables.dispatch(local) + val t = newLocal.t.asPointer.get.element + Block(Seq( + HeapLocalDecl(newLocal), + Assign( + HeapLocal[Post](newLocal.ref), + NewNonNullPointerArray(t, const(1))(PanicBlame("Size > 0")), + )(AssignLocalOk), + Assign( + newLocal.get(DerefAssignTarget), + NewObject(t.asInstanceOf[TByValueClass[Post]].cls), + )(AssignLocalOk), + )) + } + case assign: Assign[Pre] => { + val target = inAssignment.having(()) { dispatch(assign.target) } + copyContext.having(InAssignmentStatement(assign)) { + assign.rewrite(target = target) + } + } + case _ => node.rewriteDefault() + } + } + + private def copyClassValue( + obj: Expr[Post], + t: TByValueClass[Pre], + blame: InstanceField[Pre] => Blame[InsufficientPermission], + ): Expr[Post] = { + implicit val o: Origin = obj.o + val ov = new Variable[Post](obj.t) + val v = + new Variable[Post](dispatch(t))(o.withContent(TypeName("HelloWorld"))) + val children = t.cls.decl.decls.collect { case f: InstanceField[Pre] => + f.t match { + case inner: TByValueClass[Pre] => + Assign[Post]( + DerefPointer(Deref[Post](v.get, succ(f))(DerefAssignTarget))( + NonNullPointerNull + ), + copyClassValue(Deref[Post](ov.get, succ(f))(blame(f)), inner, blame), + )(AssignLocalOk) + case _ => + Assign[Post]( + DerefPointer(Deref[Post](v.get, succ(f))(DerefAssignTarget))( + NonNullPointerNull + ), + DerefPointer(Deref[Post](ov.get, succ(f))(blame(f)))( + NonNullPointerNull + ), + )(AssignLocalOk) + + } + } + ScopedExpr( + Seq(ov, v), + Then( + With( + assignLocal(ov.get, obj), + PreAssignExpression(v.get, NewObject[Post](succ(t.cls.decl)))( + AssignLocalOk + ), + ), + Block(children), + ), + ) + } + + private def unwrapClassPerm( + obj: Expr[Post], + perm: Expr[Post], + structType: TByValueClass[Pre], + visited: Seq[TByValueClass[Pre]] = Seq(), + ): Expr[Post] = { + if (visited.contains(structType)) + throw UnsupportedStructPerm( + obj.o + ) // We do not allow this notation for recursive structs + implicit val o: Origin = obj.o + val blame = PanicBlame("Field permission is framed") + val fields = structType.cls.decl.decls.collect { + case f: InstanceField[Pre] => f + } + val newFieldPerms = fields.map(member => { + val loc = FieldLocation[Post](obj, succ(member)) + member.t.asPointer.get.element match { + case inner: TByValueClass[Pre] => + Perm[Post](loc, perm) &* unwrapClassPerm( + DerefPointer(Deref[Post](obj, succ(member))(blame))( + NonNullPointerNull + ), + perm, + inner, + structType +: visited, + ) + case _ => Perm(loc, perm) + } + }) + + foldStar(newFieldPerms) + } + + override def dispatch(node: Expr[Pre]): Expr[Post] = { + implicit val o: Origin = node.o + if (inAssignment.nonEmpty) + node.rewriteDefault() + else + node match { + case Perm(ByValueClassLocation(e), p) => + unwrapClassPerm( + dispatch(e), + dispatch(p), + e.t.asInstanceOf[TByValueClass[Pre]], + ) + // What if I get rid of this... +// case Perm(loc@PointerLocation(e), p) if e.t.asPointer.exists(t => t.element.isInstanceOf[TByValueClass[Pre]])=> +// unwrapClassPerm(DerefPointer(dispatch(e))(PointerLocationDerefBlame(loc.blame))(loc.o), dispatch(p), e.t.asPointer.get.element.asInstanceOf[TByValueClass[Pre]]) + case assign: PreAssignExpression[Pre] => + val target = inAssignment.having(()) { dispatch(assign.target) } + copyContext.having(InAssignmentExpression(assign)) { + assign.rewrite(target = target) + } + case invocation: Invocation[Pre] => { + copyContext.having(InCall(invocation)) { invocation.rewriteDefault() } + } + // WHOOPSIE WE ALSO MAKE A COPY IF IT WAS A POINTER + case dp @ DerefPointer(HeapLocal(Ref(v))) + if v.t.asPointer.get.element.isInstanceOf[TByValueClass[Pre]] => + rewriteInCopyContext( + dp, + v.t.asPointer.get.element.asInstanceOf[TByValueClass[Pre]], + ) + case dp @ DerefPointer(DerefHeapVariable(Ref(v))) + if v.t.asPointer.get.element.isInstanceOf[TByValueClass[Pre]] => + rewriteInCopyContext( + dp, + v.t.asPointer.get.element.asInstanceOf[TByValueClass[Pre]], + ) + case dp @ DerefPointer(Deref(_, Ref(f))) + if f.t.asPointer.get.element.isInstanceOf[TByValueClass[Pre]] => + rewriteInCopyContext( + dp, + f.t.asPointer.get.element.asInstanceOf[TByValueClass[Pre]], + ) + case dp @ DerefPointer(Local(Ref(v))) + if v.t.asPointer.get.element.isInstanceOf[TByValueClass[Pre]] => + // This can happen if the user specifies a local of type pointer to TByValueClass + rewriteInCopyContext( + dp, + v.t.asPointer.get.element.asInstanceOf[TByValueClass[Pre]], + ) + case _ => node.rewriteDefault() + } + } + + private def rewriteInCopyContext( + dp: DerefPointer[Pre], + t: TByValueClass[Pre], + ): Expr[Post] = { + if (copyContext.isEmpty) { + // If we are in other kinds of expressions like if statements + return dp.rewriteDefault() + } + val clazz = t.cls.decl.asInstanceOf[ByValueClass[Pre]] + + copyContext.top match { + case InCall(invocation) => + copyClassValue( + dp.rewriteDefault(), + t, + f => ClassCopyInCallFailed(dp.blame, invocation, clazz, f), + ) + case InAssignmentExpression(assignment) => + copyClassValue( + dp.rewriteDefault(), + t, + f => ClassCopyInAssignmentFailed(dp.blame, assignment, clazz, f), + ) + case InAssignmentStatement(assignment) => + copyClassValue( + dp.rewriteDefault(), + t, + f => ClassCopyInAssignmentFailed(dp.blame, assignment, clazz, f), + ) + } + } +} diff --git a/src/rewrite/vct/rewrite/ResolveExpressionSideEffects.scala b/src/rewrite/vct/rewrite/ResolveExpressionSideEffects.scala index ba090ae05e..8fca4ff97a 100644 --- a/src/rewrite/vct/rewrite/ResolveExpressionSideEffects.scala +++ b/src/rewrite/vct/rewrite/ResolveExpressionSideEffects.scala @@ -340,6 +340,7 @@ case class ResolveExpressionSideEffects[Pre <: Generation]() ), ) case decl: LocalDecl[Pre] => rewriteDefault(decl) + case decl: HeapLocalDecl[Pre] => decl.rewriteDefault() case Return(result) => frame( result, @@ -532,6 +533,7 @@ case class ResolveExpressionSideEffects[Pre <: Generation]() val result = target match { case Local(Ref(v)) => Local[Post](succ(v))(target.o) + case HeapLocal(Ref(v)) => HeapLocal[Post](succ(v))(target.o) case deref @ DerefHeapVariable(Ref(v)) => DerefHeapVariable[Post](succ(v))(deref.blame)(target.o) case Deref(obj, Ref(f)) => diff --git a/src/rewrite/vct/rewrite/TrivialAddrOf.scala b/src/rewrite/vct/rewrite/TrivialAddrOf.scala index edc400f193..63b5396c0d 100644 --- a/src/rewrite/vct/rewrite/TrivialAddrOf.scala +++ b/src/rewrite/vct/rewrite/TrivialAddrOf.scala @@ -39,7 +39,7 @@ case class TrivialAddrOf[Pre <: Generation]() extends Rewriter[Pre] { case AddrOf(other) => throw UnsupportedLocation(other) case assign @ PreAssignExpression(target, AddrOf(value)) - if value.t.isInstanceOf[TClass[Pre]] => + if value.t.isInstanceOf[TByReferenceClass[Pre]] => implicit val o: Origin = assign.o val (newPointer, newTarget, newValue) = rewriteAssign( target, @@ -61,7 +61,7 @@ case class TrivialAddrOf[Pre <: Generation]() extends Rewriter[Pre] { override def dispatch(s: Statement[Pre]): Statement[Post] = s match { case assign @ Assign(target, AddrOf(value)) - if value.t.isInstanceOf[TClass[Pre]] => + if value.t.isInstanceOf[TByReferenceClass[Pre]] => implicit val o: Origin = assign.o val (newPointer, newTarget, newValue) = rewriteAssign( target, diff --git a/src/rewrite/vct/rewrite/VariableToPointer.scala b/src/rewrite/vct/rewrite/VariableToPointer.scala new file mode 100644 index 0000000000..ab399c63a5 --- /dev/null +++ b/src/rewrite/vct/rewrite/VariableToPointer.scala @@ -0,0 +1,221 @@ +package vct.rewrite + +import vct.col.ast._ +import vct.col.ref._ +import vct.col.origin._ +import vct.col.rewrite.{Generation, Rewriter, RewriterBuilder, Rewritten} +import vct.col.util.AstBuildHelpers._ +import vct.col.util.SuccessionMap +import vct.result.VerificationError.UserError + +import scala.collection.mutable + +case object VariableToPointer extends RewriterBuilder { + override def key: String = "variableToPointer" + + override def desc: String = + "Translate every local and field to a pointer such that it can have its address taken" + + case class UnsupportedAddrOf(loc: Expr[_]) extends UserError { + override def code: String = "unsupportedAddrOf" + + override def text: String = + loc.o.messageInContext( + "Taking an address of this expression is not supported" + ) + } +} + +case class VariableToPointer[Pre <: Generation]() extends Rewriter[Pre] { + + import VariableToPointer._ + + val addressedSet: mutable.Set[Node[Pre]] = new mutable.HashSet[Node[Pre]]() + val heapVariableMap: SuccessionMap[HeapVariable[Pre], HeapVariable[Post]] = + SuccessionMap() + val variableMap: SuccessionMap[Variable[Pre], Variable[Post]] = + SuccessionMap() + val fieldMap: SuccessionMap[InstanceField[Pre], InstanceField[Post]] = + SuccessionMap() + + override def dispatch(program: Program[Pre]): Program[Rewritten[Pre]] = { + // TODO: Replace the isInstanceOf[TByReferenceClass] checks with something that more clearly communicates that we want to exclude all reference types + addressedSet.addAll(program.collect { + case AddrOf(Local(Ref(v))) if !v.t.isInstanceOf[TByReferenceClass[Pre]] => + v + case AddrOf(DerefHeapVariable(Ref(v))) + if !v.t.isInstanceOf[TByReferenceClass[Pre]] => + v + case AddrOf(Deref(_, Ref(f))) + if !f.t.isInstanceOf[TByReferenceClass[Pre]] => + f + }) + super.dispatch(program) + } + + override def dispatch(decl: Declaration[Pre]): Unit = + decl match { + // TODO: Use some sort of NonNull pointer type instead + case v: HeapVariable[Pre] if addressedSet.contains(v) => + heapVariableMap(v) = globalDeclarations + .succeed(v, new HeapVariable(TPointer(dispatch(v.t)))(v.o)) + case v: Variable[Pre] if addressedSet.contains(v) => + variableMap(v) = variables + .succeed(v, new Variable(TPointer(dispatch(v.t)))(v.o)) + case f: InstanceField[Pre] if addressedSet.contains(f) => + fieldMap(f) = classDeclarations.succeed( + f, + new InstanceField( + TPointer(dispatch(f.t)), + f.flags.map { it => dispatch(it) }, + )(f.o), + ) + case other => allScopes.anySucceed(other, other.rewriteDefault()) + } + + override def dispatch(stat: Statement[Pre]): Statement[Post] = { + implicit val o: Origin = stat.o + stat match { + case s: Scope[Pre] => + s.rewrite( + locals = variables.dispatch(s.locals), + body = Block(s.locals.filter { local => addressedSet.contains(local) } + .map { local => + implicit val o: Origin = local.o + Assign( + Local[Post](variableMap.ref(local)), + NewPointerArray( + variableMap(local).t.asPointer.get.element, + const(1), + )(PanicBlame("Size is > 0")), + )(PanicBlame("Initialisation should always succeed")) + } ++ Seq(dispatch(s.body))), + ) + case i @ Instantiate(cls, out) => + // TODO: Make sure that we recursively build newobject for byvalueclasses + // maybe get rid this entirely and only have it in encode by value class + Block(Seq(i.rewriteDefault()) ++ cls.decl.declarations.flatMap { + case f: InstanceField[Pre] => + if (f.t.asClass.isDefined) { + Seq( + Assign( + Deref[Post](dispatch(out), fieldMap.ref(f))(PanicBlame( + "Initialisation should always succeed" + )), + NewPointerArray( + fieldMap(f).t.asPointer.get.element, + const(1), + )(PanicBlame("Size is > 0")), + )(PanicBlame("Initialisation should always succeed")), + Assign( + PointerSubscript( + Deref[Post](dispatch(out), fieldMap.ref(f))(PanicBlame( + "Initialisation should always succeed" + )), + const[Post](0), + )(PanicBlame("Size is > 0")), + dispatch(NewObject[Pre](f.t.asClass.get.cls)), + )(PanicBlame("Initialisation should always succeed")), + ) + } else if (addressedSet.contains(f)) { + Seq( + Assign( + Deref[Post](dispatch(out), fieldMap.ref(f))(PanicBlame( + "Initialisation should always succeed" + )), + NewPointerArray( + fieldMap(f).t.asPointer.get.element, + const(1), + )(PanicBlame("Size is > 0")), + )(PanicBlame("Initialisation should always succeed")) + ) + } else { Seq() } + case _ => Seq() + }) + case other => other.rewriteDefault() + } + } + + override def dispatch(expr: Expr[Pre]): Expr[Post] = { + implicit val o: Origin = expr.o + expr match { + case deref @ DerefHeapVariable(Ref(v)) if addressedSet.contains(v) => + DerefPointer( + DerefHeapVariable[Post](heapVariableMap.ref(v))(deref.blame) + )(PanicBlame("Should always be accessible")) + case Local(Ref(v)) if addressedSet.contains(v) => + DerefPointer(Local[Post](variableMap.ref(v)))(PanicBlame( + "Should always be accessible" + )) + case deref @ Deref(obj, Ref(f)) if addressedSet.contains(f) => + DerefPointer(Deref[Post](dispatch(obj), fieldMap.ref(f))(deref.blame))( + PanicBlame("Should always be accessible") + ) + case newObject @ NewObject(Ref(cls)) => + val obj = new Variable[Post](TByReferenceClass(succ(cls), Seq())) + ScopedExpr( + Seq(obj), + With( + Block( + Seq(assignLocal(obj.get, newObject.rewriteDefault())) ++ + cls.declarations.flatMap { + case f: InstanceField[Pre] => + if (f.t.asClass.isDefined) { + Seq( + Assign( + Deref[Post](obj.get, anySucc(f))(PanicBlame( + "Initialisation should always succeed" + )), + dispatch(NewObject[Pre](f.t.asClass.get.cls)), + )(PanicBlame("Initialisation should always succeed")) + ) + } else if (addressedSet.contains(f)) { + Seq( + Assign( + Deref[Post](obj.get, fieldMap.ref(f))(PanicBlame( + "Initialisation should always succeed" + )), + NewPointerArray( + fieldMap(f).t.asPointer.get.element, + const(1), + )(PanicBlame("Size is > 0")), + )(PanicBlame("Initialisation should always succeed")) + ) + } else { Seq() } + case _ => Seq() + } + ), + obj.get, + ), + ) + case other => other.rewriteDefault() + } + } + + override def dispatch(loc: Location[Pre]): Location[Post] = { + implicit val o: Origin = loc.o + loc match { + case HeapVariableLocation(Ref(v)) if addressedSet.contains(v) => + PointerLocation( + DerefHeapVariable[Post](heapVariableMap.ref(v))(PanicBlame( + "Should always be accessible" + )) + )(PanicBlame("Should always be accessible")) + case FieldLocation(obj, Ref(f)) if addressedSet.contains(f) => + PointerLocation(Deref[Post](dispatch(obj), fieldMap.ref(f))(PanicBlame( + "Should always be accessible" + )))(PanicBlame("Should always be accessible")) + case PointerLocation( + AddrOf(Deref(obj, Ref(f))) + ) /* if addressedSet.contains(f) always true */ => + FieldLocation[Post](dispatch(obj), fieldMap.ref(f)) + case PointerLocation( + AddrOf(DerefHeapVariable(Ref(v))) + ) /* if addressedSet.contains(v) always true */ => + HeapVariableLocation[Post](heapVariableMap.ref(v)) + case PointerLocation(AddrOf(local @ Local(_))) => + throw UnsupportedAddrOf(local) + case other => other.rewriteDefault() + } + } +} diff --git a/src/rewrite/vct/rewrite/adt/ImportPointer.scala b/src/rewrite/vct/rewrite/adt/ImportPointer.scala index 2e81faf964..09f85ec8eb 100644 --- a/src/rewrite/vct/rewrite/adt/ImportPointer.scala +++ b/src/rewrite/vct/rewrite/adt/ImportPointer.scala @@ -87,17 +87,30 @@ case class ImportPointer[Pre <: Generation](importer: ImportADTImporter) ).ref } + private def unwrapOption( + ptr: Expr[Pre], + blame: Blame[PointerNull], + ): Expr[Post] = { + ptr.t match { + case TPointer(_) => + OptGet(dispatch(ptr))(PointerNullOptNone(blame, ptr))(ptr.o) + case TNonNullPointer(_) => dispatch(ptr) + } + } + override def applyCoercion(e: => Expr[Post], coercion: Coercion[Pre])( implicit o: Origin ): Expr[Post] = coercion match { case CoerceNullPointer(_) => OptNone() + case CoerceNonNullPointer(_) => OptSome(e) case other => super.applyCoercion(e, other) } override def postCoerce(t: Type[Pre]): Type[Post] = t match { case TPointer(_) => TOption(TAxiomatic(pointerAdt.ref, Nil)) + case TNonNullPointer(_) => TAxiomatic(pointerAdt.ref, Nil) case other => rewriteDefault(other) } @@ -108,11 +121,7 @@ case class ImportPointer[Pre <: Generation](importer: ImportADTImporter) obj = FunctionInvocation[Post]( ref = pointerDeref.ref, - args = Seq( - OptGet(dispatch(pointer))( - PointerNullOptNone(loc.blame, pointer) - )(pointer.o) - ), + args = Seq(unwrapOption(pointer, loc.blame)), typeArgs = Nil, Nil, Nil, @@ -133,12 +142,7 @@ case class ImportPointer[Pre <: Generation](importer: ImportADTImporter) args = Seq( FunctionInvocation[Post]( ref = pointerAdd.ref, - args = Seq( - OptGet(dispatch(pointer))( - PointerNullOptNone(sub.blame, pointer) - ), - dispatch(index), - ), + args = Seq(unwrapOption(pointer, sub.blame), dispatch(index)), typeArgs = Nil, Nil, Nil, @@ -151,46 +155,51 @@ case class ImportPointer[Pre <: Generation](importer: ImportADTImporter) field = getPointerField(pointer), )(PointerFieldInsufficientPermission(sub.blame, sub)) case add @ PointerAdd(pointer, offset) => - OptSome( + val inv = FunctionInvocation[Post]( ref = pointerAdd.ref, - args = Seq( - OptGet(dispatch(pointer))(PointerNullOptNone(add.blame, pointer)), - dispatch(offset), - ), + args = Seq(unwrapOption(pointer, add.blame), dispatch(offset)), typeArgs = Nil, Nil, Nil, )(NoContext(PointerBoundsPreconditionFailed(add.blame, pointer))) - ) + pointer.t match { + case TPointer(_) => OptSome(inv) + case TNonNullPointer(_) => inv + } case deref @ DerefPointer(pointer) => - SilverDeref( - obj = - FunctionInvocation[Post]( - ref = pointerDeref.ref, - args = Seq( - FunctionInvocation[Post]( - ref = pointerAdd.ref, - // Always index with zero, otherwise quantifiers with pointers do not get triggered - args = Seq( - OptGet(dispatch(pointer))( - PointerNullOptNone(deref.blame, pointer) - ), - const(0), - ), - typeArgs = Nil, - Nil, - Nil, - )(NoContext( - DerefPointerBoundsPreconditionFailed(deref.blame, pointer) - )) - ), - typeArgs = Nil, - Nil, - Nil, - )(PanicBlame("ptr_deref requires nothing.")), - field = getPointerField(pointer), - )(PointerFieldInsufficientPermission(deref.blame, deref)) + if (pointer.o.find[TypeName].isDefined) { + FunctionInvocation[Post]( + ref = pointerDeref.ref, + args = Seq(unwrapOption(pointer, deref.blame)), + typeArgs = Nil, + Nil, + Nil, + )(PanicBlame("ptr_deref requires nothing.")) + } else { + SilverDeref( + obj = + FunctionInvocation[Post]( + ref = pointerDeref.ref, + args = Seq( + FunctionInvocation[Post]( + ref = pointerAdd.ref, + // Always index with zero, otherwise quantifiers with pointers do not get triggered + args = Seq(unwrapOption(pointer, deref.blame), const(0)), + typeArgs = Nil, + Nil, + Nil, + )(NoContext( + DerefPointerBoundsPreconditionFailed(deref.blame, pointer) + )) + ), + typeArgs = Nil, + Nil, + Nil, + )(PanicBlame("ptr_deref requires nothing.")), + field = getPointerField(pointer), + )(PointerFieldInsufficientPermission(deref.blame, deref)) + } case len @ PointerBlockLength(pointer) => ADTFunctionInvocation[Post]( typeArgs = Some((blockAdt.ref, Nil)), @@ -198,18 +207,14 @@ case class ImportPointer[Pre <: Generation](importer: ImportADTImporter) args = Seq(ADTFunctionInvocation[Post]( typeArgs = Some((pointerAdt.ref, Nil)), ref = pointerBlock.ref, - args = Seq( - OptGet(dispatch(pointer))(PointerNullOptNone(len.blame, pointer)) - ), + args = Seq(unwrapOption(pointer, len.blame)), )), ) case off @ PointerBlockOffset(pointer) => ADTFunctionInvocation[Post]( typeArgs = Some((pointerAdt.ref, Nil)), ref = pointerOffset.ref, - args = Seq( - OptGet(dispatch(pointer))(PointerNullOptNone(off.blame, pointer)) - ), + args = Seq(unwrapOption(pointer, off.blame)), ) case pointerLen @ PointerLength(pointer) => postCoerce( diff --git a/src/rewrite/vct/rewrite/lang/LangCToCol.scala b/src/rewrite/vct/rewrite/lang/LangCToCol.scala index f4ca25352c..955e57c7bd 100644 --- a/src/rewrite/vct/rewrite/lang/LangCToCol.scala +++ b/src/rewrite/vct/rewrite/lang/LangCToCol.scala @@ -254,7 +254,8 @@ case class LangCToCol[Pre <: Generation](rw: LangSpecificToCol[Pre]) SuccessionMap() val cNameSuccessor: SuccessionMap[CNameTarget[Pre], Variable[Post]] = SuccessionMap() - val cLocalHeapNameSuccessor: SuccessionMap[CNameTarget[Pre], LocalHeapVariable[Post]] = + val cLocalHeapNameSuccessor + : SuccessionMap[CNameTarget[Pre], LocalHeapVariable[Post]] = SuccessionMap() val cGlobalNameSuccessor : SuccessionMap[CNameTarget[Pre], HeapVariable[Post]] = SuccessionMap() @@ -991,10 +992,9 @@ case class LangCToCol[Pre <: Generation](rw: LangSpecificToCol[Pre]) Seq(x), ) = fieldDecl fieldDecl.drop() - val t = - specs.collectFirst { case t: CSpecificationType[Pre] => - rw.dispatch(t.t) - }.get + val t = TNonNullPointer(specs.collectFirst { + case t: CSpecificationType[Pre] => rw.dispatch(t.t) + }.get) cStructFieldsSuccessor((decl, fieldDecl)) = new InstanceField(t = t, flags = Nil)(CStructFieldOrigin(x)) rw.classDeclarations @@ -1147,11 +1147,20 @@ case class LangCToCol[Pre <: Generation](rw: LangSpecificToCol[Pre]) val targetClass: Class[Post] = cStructSuccessor(ref.decl) val t = TByValueClass[Post](targetClass.ref, Seq()) - val v = new LocalHeapVariable[Post](t)(o.sourceName(info.name)) + val v = + new LocalHeapVariable[Post](TNonNullPointer(t))(o.sourceName(info.name)) cLocalHeapNameSuccessor(RefCLocalDeclaration(decl, 0)) = v if (init.init.isDefined) { - Block(Seq(HeapLocalDecl(v), assignHeapLocal(v.get, rw.dispatch(init.init.get)))) + Block(Seq( + HeapLocalDecl(v), + Assign( + v.get(PanicBlame( + "Dereferencing freshly declared struct should never fail" + )), + rw.dispatch(init.init.get), + )(AssignLocalOk), + )) } else { HeapLocalDecl(v) } } @@ -1315,7 +1324,11 @@ case class LangCToCol[Pre <: Generation](rw: LangSpecificToCol[Pre]) DerefHeapVariable[Post](cGlobalNameSuccessor.ref(ref))(local.blame) case Some(_) => throw NotAValue(local) } - case ref: RefCLocalDeclaration[Pre] if cLocalHeapNameSuccessor.contains(ref) => HeapLocal(cLocalHeapNameSuccessor.ref(ref)) + case ref: RefCLocalDeclaration[Pre] + if cLocalHeapNameSuccessor.contains(ref) => + DerefPointer(HeapLocal[Post](cLocalHeapNameSuccessor.ref(ref)))( + local.blame + ) case ref: RefCLocalDeclaration[Pre] => Local(cNameSuccessor.ref(ref)) case _: RefCudaVec[Pre] => throw NotAValue(local) } @@ -1365,10 +1378,12 @@ case class LangCToCol[Pre <: Generation](rw: LangSpecificToCol[Pre]) case _: TNotAValue[Pre] => throw TypeUsedAsValue(deref.obj) case _ => ??? } - Deref[Post]( - rw.dispatch(deref.obj), - cStructFieldsSuccessor.ref((struct_ref.decl, struct.decls)), - )(deref.blame) + DerefPointer( + Deref[Post]( + rw.dispatch(deref.obj), + cStructFieldsSuccessor.ref((struct_ref.decl, struct.decls)), + )(deref.blame) + )(NonNullPointerNull) } } @@ -1388,10 +1403,12 @@ case class LangCToCol[Pre <: Generation](rw: LangSpecificToCol[Pre]) case CTPointer(CTStruct(struct)) => struct case t => throw WrongStructType(t) } - Deref[Post]( - DerefPointer(rw.dispatch(deref.struct))(b), - cStructFieldsSuccessor.ref((structRef.decl, struct.decls)), - )(deref.blame)(deref.o) + DerefPointer( + Deref[Post]( + DerefPointer(rw.dispatch(deref.struct))(b), + cStructFieldsSuccessor.ref((structRef.decl, struct.decls)), + )(deref.blame)(deref.o) + )(NonNullPointerNull)(deref.o) } } @@ -1415,9 +1432,11 @@ case class LangCToCol[Pre <: Generation](rw: LangSpecificToCol[Pre]) val newFieldPerms = fields.map(member => { val loc = AmbiguousLocation( - Deref[Post]( - newExpr, - cStructFieldsSuccessor.ref((structType.ref.decl, member)), + DerefPointer( + Deref[Post]( + newExpr, + cStructFieldsSuccessor.ref((structType.ref.decl, member)), + )(blame) )(blame) )(struct.blame) member.specs.collectFirst { diff --git a/src/rewrite/vct/rewrite/lang/LangSpecificToCol.scala b/src/rewrite/vct/rewrite/lang/LangSpecificToCol.scala index 488782e83b..b15ae0c8b9 100644 --- a/src/rewrite/vct/rewrite/lang/LangSpecificToCol.scala +++ b/src/rewrite/vct/rewrite/lang/LangSpecificToCol.scala @@ -332,17 +332,17 @@ case class LangSpecificToCol[Pre <: Generation]( case cast: CCast[Pre] => c.cast(cast) case sizeof: SizeOf[Pre] => throw LangCToCol.UnsupportedSizeof(sizeof) - case Perm(a @ AmbiguousLocation(expr), perm) - if c.getBaseType(expr.t).isInstanceOf[CTStruct[Pre]] => - c.getBaseType(expr.t) match { - case structType: CTStruct[Pre] => - c.unwrapStructPerm( - dispatch(a).asInstanceOf[AmbiguousLocation[Post]], - perm, - structType, - e.o, - ) - } +// case Perm(a @ AmbiguousLocation(expr), perm) +// if c.getBaseType(expr.t).isInstanceOf[CTStruct[Pre]] => +// c.getBaseType(expr.t) match { +// case structType: CTStruct[Pre] => +// c.unwrapStructPerm( +// dispatch(a).asInstanceOf[AmbiguousLocation[Post]], +// perm, +// structType, +// e.o, +// ) +// } case local: CPPLocal[Pre] => cpp.local(local) case deref: CPPClassMethodOrFieldAccess[Pre] => cpp.deref(deref) case inv: CPPInvocation[Pre] => cpp.invocation(inv) From 1666b3577c1eb1969243c89ff940caa28bf87959 Mon Sep 17 00:00:00 2001 From: Alexander Stekelenburg Date: Mon, 17 Jun 2024 17:16:06 +0200 Subject: [PATCH 03/26] Add some axioms that speed up pointer verification --- build.sc | 12 +- res/universal/res/adt/pointer.pvl | 14 +- src/col/vct/col/ast/Node.scala | 4 + .../expr/heap/read/RawDerefPointerImpl.scala | 14 ++ .../vct/col/typerules/CoercingRewriter.scala | 2 + src/main/vct/main/stages/Transformation.scala | 3 +- src/rewrite/vct/rewrite/ClassToRef.scala | 68 ++++--- .../vct/rewrite/DisambiguateLocation.scala | 8 +- .../vct/rewrite/EncodeArrayValues.scala | 3 +- .../vct/rewrite/PrepareByValueClass.scala | 76 +++++++- .../vct/rewrite/adt/ImportPointer.scala | 173 ++++++++++++++---- src/rewrite/vct/rewrite/lang/LangCToCol.scala | 21 ++- 12 files changed, 308 insertions(+), 90 deletions(-) create mode 100644 src/col/vct/col/ast/expr/heap/read/RawDerefPointerImpl.scala diff --git a/build.sc b/build.sc index 23a2c215fa..2e7c847df6 100644 --- a/build.sc +++ b/build.sc @@ -41,7 +41,7 @@ object external extends Module { object viper extends ScalaModule { object silverGit extends GitModule { def url = T { "https://github.com/viperproject/silver.git" } - def commitish = T { "31c94df4f9792046618d9b4db52444ffe9c7c988" } + def commitish = T { "9cd85c01c10f5f846ed2754d64de08fcc59207ee" } def filteredRepo = T { val workspace = repo() os.remove.all(workspace / "src" / "test") @@ -51,7 +51,7 @@ object viper extends ScalaModule { object siliconGit extends GitModule { def url = T { "https://github.com/viperproject/silicon.git" } - def commitish = T { "529d2a49108b954d2b0749356faf985d622f54f0" } + def commitish = T { "c4350bd33043a727a0a4f3008f39a4efc7748033" } def filteredRepo = T { val workspace = repo() os.remove.all(workspace / "src" / "test") @@ -61,7 +61,7 @@ object viper extends ScalaModule { object carbonGit extends GitModule { def url = T { "https://github.com/viperproject/carbon.git" } - def commitish = T { "d7ac8b000e1123a72cbdda0c7679ab88ca8a52d4" } + def commitish = T { "15d74246bb8baef1e3ea88dcc4861c891259a99d" } } object silver extends ScalaModule { @@ -79,6 +79,8 @@ object viper extends ScalaModule { ivy"commons-io:commons-io:2.8.0", ivy"com.google.guava:guava:29.0-jre", ivy"org.jgrapht:jgrapht-core:1.5.0", + ivy"com.lihaoyi::requests:0.3.0", + ivy"com.lihaoyi::upickle:1.0.0", ) } @@ -407,14 +409,14 @@ object vercors extends Module { ) override def moduleDeps = Seq(hre, col, serialize) - val includeVcllvmCross = interp.watchValue { + val includeVcllvmCross = interp.watchValue { if(os.exists(settings.root / ".include-vcllvm")) { Seq("vcllvm") } else { Seq.empty[String] } } - + object vcllvmDep extends Cross[VcllvmDep](includeVcllvmCross) trait VcllvmDep extends Cross.Module[String] { def path = T { diff --git a/res/universal/res/adt/pointer.pvl b/res/universal/res/adt/pointer.pvl index 743584d8b4..6c4da7e5db 100644 --- a/res/universal/res/adt/pointer.pvl +++ b/res/universal/res/adt/pointer.pvl @@ -16,6 +16,7 @@ adt `pointer` { pure `pointer` pointer_of(`block` b, int offset); pure `block` pointer_block(`pointer` p); pure int pointer_offset(`pointer` p); + pure `pointer` pointer_inv(ref r); // the block offset is valid wrt the length of the block axiom (∀ `pointer` p; @@ -26,6 +27,17 @@ adt `pointer` { axiom (∀`block` b, int offset; {:pointer_block(pointer_of(b, offset)):} == b && {:pointer_offset(pointer_of(b, offset)):} == offset); + + axiom (∀ ref r; ptr_deref({:pointer_inv(r):}) == r); + + axiom (∀ `pointer` p; pointer_inv({:ptr_deref(p):}) == p); + + axiom (∀ `pointer` p1, `pointer` p2, int offset; + (0 <= offset && offset < `block`.block_length(pointer_block(p1)) && + pointer_block(p1) == pointer_block(p2) && + {:pointer_of(pointer_block(p1), offset):} == + {:pointer_of(pointer_block(p2), offset):}) ==> p1 == p2 + ); } decreases; @@ -38,4 +50,4 @@ requires `pointer`.pointer_offset(p) + offset < `block`.block_length(`pointer`.p pure `pointer` ptr_add(`pointer` p, int offset) = `pointer`.pointer_of( `pointer`.pointer_block(p), - `pointer`.pointer_offset(p) + offset); \ No newline at end of file + `pointer`.pointer_offset(p) + offset); diff --git a/src/col/vct/col/ast/Node.scala b/src/col/vct/col/ast/Node.scala index c46d938b5b..10d52782e1 100644 --- a/src/col/vct/col/ast/Node.scala +++ b/src/col/vct/col/ast/Node.scala @@ -1400,6 +1400,10 @@ final case class DerefPointer[G](pointer: Expr[G])( val blame: Blame[PointerDerefError] )(implicit val o: Origin) extends Expr[G] with DerefPointerImpl[G] +final case class RawDerefPointer[G](pointer: Expr[G])( + val blame: Blame[PointerDerefError] +)(implicit val o: Origin) + extends Expr[G] with RawDerefPointerImpl[G] final case class PointerAdd[G](pointer: Expr[G], offset: Expr[G])( val blame: Blame[PointerAddError] )(implicit val o: Origin) diff --git a/src/col/vct/col/ast/expr/heap/read/RawDerefPointerImpl.scala b/src/col/vct/col/ast/expr/heap/read/RawDerefPointerImpl.scala new file mode 100644 index 0000000000..d270112e07 --- /dev/null +++ b/src/col/vct/col/ast/expr/heap/read/RawDerefPointerImpl.scala @@ -0,0 +1,14 @@ +package vct.col.ast.expr.heap.read + +import vct.col.ast.ops.RawDerefPointerOps +import vct.col.ast.{RawDerefPointer, TRef, Type} +import vct.col.print._ + +trait RawDerefPointerImpl[G] extends RawDerefPointerOps[G] { + this: RawDerefPointer[G] => + override def t: Type[G] = TRef() + + override def precedence: Int = Precedence.POSTFIX + override def layout(implicit ctx: Ctx): Doc = + Group(Text("ptr_deref(") <> pointer <> Text(")")) +} diff --git a/src/col/vct/col/typerules/CoercingRewriter.scala b/src/col/vct/col/typerules/CoercingRewriter.scala index 4c709d31cd..c26e6c9476 100644 --- a/src/col/vct/col/typerules/CoercingRewriter.scala +++ b/src/col/vct/col/typerules/CoercingRewriter.scala @@ -1245,6 +1245,8 @@ abstract class CoercingRewriter[Pre <: Generation]() case deref @ Deref(obj, ref) => Deref(cls(obj), ref)(deref.blame) case deref @ DerefHeapVariable(ref) => DerefHeapVariable(ref)(deref.blame) case deref @ DerefPointer(p) => DerefPointer(pointer(p)._1)(deref.blame) + case deref @ RawDerefPointer(p) => + RawDerefPointer(pointer(p)._1)(deref.blame) case Drop(xs, count) => Drop(seq(xs)._1, int(count)) case Empty(obj) => Empty(sized(obj)._1) case EmptyProcess() => EmptyProcess() diff --git a/src/main/vct/main/stages/Transformation.scala b/src/main/vct/main/stages/Transformation.scala index f75ed061ee..91644dede1 100644 --- a/src/main/vct/main/stages/Transformation.scala +++ b/src/main/vct/main/stages/Transformation.scala @@ -338,7 +338,6 @@ case class SilverTransformation( QuantifySubscriptAny, // no arr[*] IterationContractToParBlock, PropagateContextEverywhere, // inline context_everywhere into loop invariants - PrepareByValueClass, EncodeArrayValues, // maybe don't target shift lemmas on generated function for \values GivenYieldsToArgs, CheckProcessAlgebra, @@ -378,7 +377,7 @@ case class SilverTransformation( // flatten out functions in the rhs of assignments, making it harder to detect final field assignments where the // value is pure and therefore be put in the contract of the constant function. ConstantifyFinalFields, - + PrepareByValueClass, // Resolve side effects including method invocations, for encodetrythrowsignals. ResolveExpressionSideChecks, ResolveExpressionSideEffects, diff --git a/src/rewrite/vct/rewrite/ClassToRef.scala b/src/rewrite/vct/rewrite/ClassToRef.scala index a3c29fbe64..bebe3d263e 100644 --- a/src/rewrite/vct/rewrite/ClassToRef.scala +++ b/src/rewrite/vct/rewrite/ClassToRef.scala @@ -364,48 +364,46 @@ case class ClassToRef[Pre <: Generation]() extends Rewriter[Pre] { } }, )) - val nonNullAxiom = - new ADTAxiom[Post](forall( - axiomType, - body = - v => { - foldAnd(fieldFunctions.map { f => - adtFunctionInvocation[Post]( - f.ref, - None, - args = Seq(v), - ) !== Null() - }) - }, + val injectivityAxiom1 = + new ADTAxiom[Post](foralls( + Seq(axiomType, axiomType), + body = { case Seq(a0, a1) => + foldAnd(fieldFunctions.combinations(2).map { + case Seq(f0, f1) => + Neq( + adtFunctionInvocation[Post](f0.ref, args = Seq(a0)), + adtFunctionInvocation[Post](f1.ref, args = Seq(a1)), + ) + }.toSeq) + }, + triggers = { case Seq(a0, a1) => + fieldFunctions.combinations(2).map { case Seq(f0, f1) => + Seq( + adtFunctionInvocation[Post](f0.ref, None, args = Seq(a0)), + adtFunctionInvocation[Post](f1.ref, None, args = Seq(a1)), + ) + }.toSeq + }, )) - // TODO: need Non null pointer.... - val injectivityAxiom = + val injectivityAxiom2 = new ADTAxiom[Post](foralls( Seq(axiomType, axiomType), body = { case Seq(a0, a1) => - (a0 !== a1) ==> foldAnd(fieldFunctions.map { f => - DerefPointer( - adtFunctionInvocation[Post](f.ref, args = Seq(a0)) - )(NonNullPointerNull)( - o.withContent(TypeName("helloWorld")) - ) !== DerefPointer( - adtFunctionInvocation[Post](f.ref, args = Seq(a1)) - )(NonNullPointerNull)(o.withContent(TypeName("helloWorld"))) + foldAnd(fieldFunctions.map { f => + Implies( + Eq( + adtFunctionInvocation[Post](f.ref, args = Seq(a0)), + adtFunctionInvocation[Post](f.ref, args = Seq(a1)), + ), + a0 === a1, + ) }) }, triggers = { case Seq(a0, a1) => fieldFunctions.map { f => Seq( - DerefPointer( - adtFunctionInvocation[Post](f.ref, None, args = Seq(a0)) - )(NonNullPointerNull)(o.withContent(TypeName( - "helloWorld" - ))), - DerefPointer( - adtFunctionInvocation[Post](f.ref, None, args = Seq(a1)) - )(NonNullPointerNull)(o.withContent(TypeName( - "helloWorld" - ))), + adtFunctionInvocation[Post](f.ref, None, args = Seq(a0)), + adtFunctionInvocation[Post](f.ref, None, args = Seq(a1)), ) } }, @@ -416,8 +414,8 @@ case class ClassToRef[Pre <: Generation]() extends Rewriter[Pre] { Seq( constructor, destructorAxiom, -// nonNullAxiom, - injectivityAxiom, + injectivityAxiom1, + injectivityAxiom2, ) ++ fieldFunctions, Nil, ) diff --git a/src/rewrite/vct/rewrite/DisambiguateLocation.scala b/src/rewrite/vct/rewrite/DisambiguateLocation.scala index e013f80915..17e0368d9f 100644 --- a/src/rewrite/vct/rewrite/DisambiguateLocation.scala +++ b/src/rewrite/vct/rewrite/DisambiguateLocation.scala @@ -36,6 +36,10 @@ case class DisambiguateLocation[Pre <: Generation]() extends Rewriter[Pre] { implicit o: Origin ): Location[Post] = expr match { + case expr if expr.t.asPointer.isDefined => + PointerLocation(dispatch(expr))(blame) + case expr if expr.t.isInstanceOf[TByValueClass[Pre]] => + ByValueClassLocation(dispatch(expr))(blame) case DerefHeapVariable(ref) => HeapVariableLocation(succ(ref.decl)) case Deref(obj, ref) => FieldLocation(dispatch(obj), succ(ref.decl)) case ModelDeref(obj, ref) => ModelLocation(dispatch(obj), succ(ref.decl)) @@ -43,10 +47,6 @@ case class DisambiguateLocation[Pre <: Generation]() extends Rewriter[Pre] { SilverFieldLocation(dispatch(obj), succ(ref.decl)) case expr @ ArraySubscript(arr, index) => ArrayLocation(dispatch(arr), dispatch(index))(expr.blame) - case expr if expr.t.asPointer.isDefined => - PointerLocation(dispatch(expr))(blame) - case expr if expr.t.isInstanceOf[TByValueClass[Pre]] => - ByValueClassLocation(dispatch(expr))(blame) case PredicateApply(ref, args, WritePerm()) => PredicateLocation(succ(ref.decl), (args.map(dispatch))) case InstancePredicateApply(obj, ref, args, WritePerm()) => diff --git a/src/rewrite/vct/rewrite/EncodeArrayValues.scala b/src/rewrite/vct/rewrite/EncodeArrayValues.scala index a134a63a58..a3d7757469 100644 --- a/src/rewrite/vct/rewrite/EncodeArrayValues.scala +++ b/src/rewrite/vct/rewrite/EncodeArrayValues.scala @@ -500,8 +500,7 @@ case class EncodeArrayValues[Pre <: Generation]() extends Rewriter[Pre] { val zero = const[Post](0) val pre1 = zero <= i.get && i.get < size val pre2 = zero <= j.get && j.get < size - val body = - (pre1 && pre2 && (i.get !== j.get)) ==> (access(i) !== access(j)) + val body = (pre1 && pre2 && access(i) === access(j)) ==> (i.get === j.get) Forall(Seq(i, j), Seq(triggerUnique), body) } } diff --git a/src/rewrite/vct/rewrite/PrepareByValueClass.scala b/src/rewrite/vct/rewrite/PrepareByValueClass.scala index 8067a5ec3b..133ad89ec3 100644 --- a/src/rewrite/vct/rewrite/PrepareByValueClass.scala +++ b/src/rewrite/vct/rewrite/PrepareByValueClass.scala @@ -94,10 +94,11 @@ case class PrepareByValueClass[Pre <: Generation]() extends Rewriter[Pre] { val t = newLocal.t.asPointer.get.element Block(Seq( HeapLocalDecl(newLocal), - Assign( - HeapLocal[Post](newLocal.ref), - NewNonNullPointerArray(t, const(1))(PanicBlame("Size > 0")), - )(AssignLocalOk), +// Assign( +// HeapLocal[Post](newLocal.ref), +// NewNonNullPointerArray(t, const(1))(PanicBlame("Size > 0")), +// )(AssignLocalOk), + // TODO: Only do this if the first use does not overwrite it again (do something similar to what I implemented in ImportPointer).... Assign( newLocal.get(DerefAssignTarget), NewObject(t.asInstanceOf[TByValueClass[Post]].cls), @@ -175,6 +176,7 @@ case class PrepareByValueClass[Pre <: Generation]() extends Rewriter[Pre] { } val newFieldPerms = fields.map(member => { val loc = FieldLocation[Post](obj, succ(member)) + // TODO: Don't go through regular pointers... member.t.asPointer.get.element match { case inner: TByValueClass[Pre] => Perm[Post](loc, perm) &* unwrapClassPerm( @@ -192,8 +194,63 @@ case class PrepareByValueClass[Pre <: Generation]() extends Rewriter[Pre] { foldStar(newFieldPerms) } + private def unwrapClassComp( + comp: (Expr[Post], Expr[Post]) => Expr[Post], + left: Expr[Post], + right: Expr[Post], + structType: TByValueClass[Pre], + visited: Seq[TByValueClass[Pre]] = Nil, + )(implicit o: Origin): Expr[Post] = { + // TODO: Better error + if (visited.contains(structType)) + throw UnsupportedStructPerm(o) + + val blame = PanicBlame("Struct deref can never fail") + val fields = structType.cls.decl.decls.collect { + case f: InstanceField[Pre] => f + } + foldAnd(fields.map(member => { + val l = + RawDerefPointer(Deref[Post](left, succ(member))(blame))( + NonNullPointerNull + ) + val r = + RawDerefPointer(Deref[Post](right, succ(member))(blame))( + NonNullPointerNull + ) + member.t match { +// case p: TNonNullPointer[Pre] if p.element.isInstanceOf[TByValueClass[Pre]] => +// unwrapClassComp(comp, DerefPointer(l)(NonNullPointerNull), r, p.element.asInstanceOf[TByValueClass[Pre]], structType +: visited) + case _ => comp(l, r) + } + })) + } + override def dispatch(node: Expr[Pre]): Expr[Post] = { implicit val o: Origin = node.o + node match { + case Eq(left, right) + if left.t == right.t && left.t.isInstanceOf[TByValueClass[Pre]] => + val newLeft = dispatch(left) + val newRight = dispatch(right) + return Eq(newLeft, newRight) && unwrapClassComp( + (l, r) => Eq(l, r), + newLeft, + newRight, + left.t.asInstanceOf[TByValueClass[Pre]], + ) + case Neq(left, right) + if left.t == right.t && left.t.isInstanceOf[TByValueClass[Pre]] => + val newLeft = dispatch(left) + val newRight = dispatch(right) + return Neq(newLeft, newRight) && unwrapClassComp( + (l, r) => Neq(l, r), + newLeft, + newRight, + left.t.asInstanceOf[TByValueClass[Pre]], + ) + case _ => {} + } if (inAssignment.nonEmpty) node.rewriteDefault() else @@ -204,6 +261,17 @@ case class PrepareByValueClass[Pre <: Generation]() extends Rewriter[Pre] { dispatch(p), e.t.asInstanceOf[TByValueClass[Pre]], ) + case Perm(pl @ PointerLocation(dhv @ DerefHeapVariable(Ref(v))), p) + if v.t.isInstanceOf[TNonNullPointer[Pre]] => + val t = v.t.asInstanceOf[TNonNullPointer[Pre]] + if (t.element.isInstanceOf[TByValueClass[Pre]]) { + val newV: Ref[Post, HeapVariable[Post]] = succ(v) + val newP = dispatch(p) + Perm(HeapVariableLocation(newV), newP) &* Perm( + PointerLocation(DerefHeapVariable(newV)(dhv.blame))(pl.blame), + newP, + ) + } else { node.rewriteDefault() } // What if I get rid of this... // case Perm(loc@PointerLocation(e), p) if e.t.asPointer.exists(t => t.element.isInstanceOf[TByValueClass[Pre]])=> // unwrapClassPerm(DerefPointer(dispatch(e))(PointerLocationDerefBlame(loc.blame))(loc.o), dispatch(p), e.t.asPointer.get.element.asInstanceOf[TByValueClass[Pre]]) diff --git a/src/rewrite/vct/rewrite/adt/ImportPointer.scala b/src/rewrite/vct/rewrite/adt/ImportPointer.scala index 09f85ec8eb..dfa103b0f1 100644 --- a/src/rewrite/vct/rewrite/adt/ImportPointer.scala +++ b/src/rewrite/vct/rewrite/adt/ImportPointer.scala @@ -5,7 +5,8 @@ import ImportADT.typeText import vct.col.origin._ import vct.col.ref.Ref import vct.col.rewrite.Generation -import vct.col.util.AstBuildHelpers.{ExprBuildHelpers, const} +import vct.col.util.AstBuildHelpers._ +import vct.col.util.SuccessionMap import scala.collection.mutable @@ -13,6 +14,10 @@ case object ImportPointer extends ImportADTBuilder("pointer") { private def PointerField(t: Type[_]): Origin = Origin(Seq(PreferredName(Seq(typeText(t))), LabelContext("pointer field"))) + private val PointerCreationOrigin: Origin = Origin( + Seq(LabelContext("classToRef, pointer creation method")) + ) + case class PointerNullOptNone(inner: Blame[PointerNull], expr: Expr[_]) extends Blame[OptionNone] { override def blame(error: OptionNone): Unit = inner.blame(PointerNull(expr)) @@ -77,6 +82,57 @@ case class ImportPointer[Pre <: Generation](importer: ImportADTImporter) val pointerField: mutable.Map[Type[Post], SilverField[Post]] = mutable.Map() + private val pointerCreationMethods + : SuccessionMap[Type[Pre], Procedure[Post]] = SuccessionMap() + + private def makePointerCreationMethod(t: Type[Post]): Procedure[Post] = { + implicit val o: Origin = PointerCreationOrigin + + val result = new Variable[Post](TAxiomatic(pointerAdt.ref, Nil)) + globalDeclarations.declare(procedure[Post]( + blame = AbstractApplicable, + contractBlame = TrueSatisfiable, + returnType = TVoid(), + outArgs = Seq(result), + ensures = UnitAccountedPredicate( + (ADTFunctionInvocation[Post]( + typeArgs = Some((blockAdt.ref, Nil)), + ref = blockLength.ref, + args = Seq(ADTFunctionInvocation[Post]( + typeArgs = Some((pointerAdt.ref, Nil)), + ref = pointerBlock.ref, + args = Seq(result.get), + )), + ) === const(1)) &* + (ADTFunctionInvocation[Post]( + typeArgs = Some((pointerAdt.ref, Nil)), + ref = pointerOffset.ref, + args = Seq(result.get), + ) === const(0)) &* Perm( + SilverFieldLocation( + obj = + FunctionInvocation[Post]( + ref = pointerDeref.ref, + args = Seq(result.get), + typeArgs = Nil, + Nil, + Nil, + )(PanicBlame("ptr_deref requires nothing.")), + field = + pointerField.getOrElseUpdate( + t, { + globalDeclarations + .declare(new SilverField(t)(PointerField(t))) + }, + ).ref, + ), + WritePerm(), + ) + ), + decreases = Some(DecreasesClauseNoRecursion[Post]()), + )) + } + private def getPointerField(ptr: Expr[Pre]): Ref[Post, SilverField[Post]] = { val tElement = dispatch(ptr.t.asPointer.get.element) pointerField.getOrElseUpdate( @@ -114,7 +170,8 @@ case class ImportPointer[Pre <: Generation](importer: ImportADTImporter) case other => rewriteDefault(other) } - override def postCoerce(location: Location[Pre]): Location[Post] = + override def postCoerce(location: Location[Pre]): Location[Post] = { + implicit val o: Origin = location.o location match { case loc @ PointerLocation(pointer) => SilverFieldLocation( @@ -127,9 +184,46 @@ case class ImportPointer[Pre <: Generation](importer: ImportADTImporter) Nil, )(PanicBlame("ptr_deref requires nothing."))(pointer.o), field = getPointerField(pointer), - )(loc.o) + ) case other => rewriteDefault(other) } + } + + override def postCoerce(s: Statement[Pre]): Statement[Post] = { + implicit val o: Origin = s.o + s match { + case scope: Scope[Pre] => + scope.rewrite(body = Block(scope.locals.collect { + case v if v.t.isInstanceOf[TNonNullPointer[Pre]] => { + val firstUse = scope.body.collectFirst { + case l @ Local(Ref(variable)) if variable == v => l + } + if ( + firstUse.isDefined && scope.body.collectFirst { + case Assign(l @ Local(Ref(variable)), _) if variable == v => + System.identityHashCode(l) != + System.identityHashCode(firstUse.get) + }.getOrElse(true) + ) { + val oldT = v.t.asInstanceOf[TNonNullPointer[Pre]].element + val newT = dispatch(oldT) + Seq( + InvokeProcedure[Post]( + pointerCreationMethods + .getOrElseUpdate(oldT, makePointerCreationMethod(newT)).ref, + Nil, + Seq(Local(succ(v))), + Nil, + Nil, + Nil, + )(TrueSatisfiable) + ) + } else { Nil } + } + }.flatten :+ dispatch(scope.body))) + case _ => s.rewriteDefault() + } + } override def postCoerce(e: Expr[Pre]): Expr[Post] = { implicit val o: Origin = e.o @@ -168,38 +262,47 @@ case class ImportPointer[Pre <: Generation](importer: ImportADTImporter) case TNonNullPointer(_) => inv } case deref @ DerefPointer(pointer) => - if (pointer.o.find[TypeName].isDefined) { - FunctionInvocation[Post]( - ref = pointerDeref.ref, - args = Seq(unwrapOption(pointer, deref.blame)), - typeArgs = Nil, - Nil, - Nil, - )(PanicBlame("ptr_deref requires nothing.")) - } else { - SilverDeref( - obj = - FunctionInvocation[Post]( - ref = pointerDeref.ref, - args = Seq( - FunctionInvocation[Post]( - ref = pointerAdd.ref, - // Always index with zero, otherwise quantifiers with pointers do not get triggered - args = Seq(unwrapOption(pointer, deref.blame), const(0)), - typeArgs = Nil, - Nil, - Nil, - )(NoContext( - DerefPointerBoundsPreconditionFailed(deref.blame, pointer) - )) - ), - typeArgs = Nil, - Nil, - Nil, - )(PanicBlame("ptr_deref requires nothing.")), - field = getPointerField(pointer), - )(PointerFieldInsufficientPermission(deref.blame, deref)) - } + SilverDeref( + obj = + FunctionInvocation[Post]( + ref = pointerDeref.ref, + args = Seq( + FunctionInvocation[Post]( + ref = pointerAdd.ref, + // Always index with zero, otherwise quantifiers with pointers do not get triggered + args = Seq(unwrapOption(pointer, deref.blame), const(0)), + typeArgs = Nil, + Nil, + Nil, + )(NoContext( + DerefPointerBoundsPreconditionFailed(deref.blame, pointer) + )) + ), + typeArgs = Nil, + Nil, + Nil, + )(PanicBlame("ptr_deref requires nothing.")), + field = getPointerField(pointer), + )(PointerFieldInsufficientPermission(deref.blame, deref)) + case deref @ RawDerefPointer(pointer) => + FunctionInvocation[Post]( + ref = pointerDeref.ref, + args = Seq( + FunctionInvocation[Post]( + ref = pointerAdd.ref, + // Always index with zero, otherwise quantifiers with pointers do not get triggered + args = Seq(unwrapOption(pointer, deref.blame), const(0)), + typeArgs = Nil, + Nil, + Nil, + )(NoContext( + DerefPointerBoundsPreconditionFailed(deref.blame, pointer) + )) + ), + typeArgs = Nil, + Nil, + Nil, + )(PanicBlame("ptr_deref requires nothing.")) case len @ PointerBlockLength(pointer) => ADTFunctionInvocation[Post]( typeArgs = Some((blockAdt.ref, Nil)), diff --git a/src/rewrite/vct/rewrite/lang/LangCToCol.scala b/src/rewrite/vct/rewrite/lang/LangCToCol.scala index 955e57c7bd..54abadbe31 100644 --- a/src/rewrite/vct/rewrite/lang/LangCToCol.scala +++ b/src/rewrite/vct/rewrite/lang/LangCToCol.scala @@ -1042,8 +1042,11 @@ case class LangCToCol[Pre <: Generation](rw: LangSpecificToCol[Pre]) ) ) case None => + val newT = + if (t.isInstanceOf[TByValueClass[Post]]) { TNonNullPointer(t) } + else { t } cGlobalNameSuccessor(RefCGlobalDeclaration(decl, idx)) = rw - .globalDeclarations.declare(new HeapVariable(t)(init.o)) + .globalDeclarations.declare(new HeapVariable(newT)(init.o)) } } } @@ -1321,7 +1324,21 @@ case class LangCToCol[Pre <: Generation](rw: LangSpecificToCol[Pre]) case ref @ RefCGlobalDeclaration(decl, initIdx) => C.getDeclaratorInfo(decl.decl.inits(initIdx).decl).params match { case None => - DerefHeapVariable[Post](cGlobalNameSuccessor.ref(ref))(local.blame) + val t = + decl.decl.specs.collectFirst { case t: CSpecificationType[Pre] => + t.t + }.get + if (t.isInstanceOf[CTStruct[Pre]]) { + DerefPointer[Post]( + DerefHeapVariable[Post](cGlobalNameSuccessor.ref(ref))( + local.blame + ) + )(NonNullPointerNull) + } else { + DerefHeapVariable[Post](cGlobalNameSuccessor.ref(ref))( + local.blame + ) + } case Some(_) => throw NotAValue(local) } case ref: RefCLocalDeclaration[Pre] From 0dfde7cdd3eda0101a56c98c2dfc90a8fd935830 Mon Sep 17 00:00:00 2001 From: Alexander Stekelenburg Date: Tue, 18 Jun 2024 15:31:39 +0200 Subject: [PATCH 04/26] Do no copy in expressions which do not yield TByValueClass --- examples/concepts/c/structs.c | 8 +----- .../vct/rewrite/EncodeArrayValues.scala | 2 +- .../vct/rewrite/PrepareByValueClass.scala | 26 ++++++++++++++----- 3 files changed, 22 insertions(+), 14 deletions(-) diff --git a/examples/concepts/c/structs.c b/examples/concepts/c/structs.c index 886ed073f5..960a6fa1ca 100644 --- a/examples/concepts/c/structs.c +++ b/examples/concepts/c/structs.c @@ -52,7 +52,6 @@ void alter_copy_struct(struct point p){ p.y = 0; } -// TODO: Should be auto-generated /*@ context Perm(p, 1\1); @*/ @@ -133,7 +132,6 @@ int main(){ struct point *pp; pp = &p; - /* //@ assert (pp[0] != NULL ); */ assert (pp != NULL ); p.x = 1; @@ -147,7 +145,7 @@ int main(){ alter_struct(pp); assert(pp->x == 0); assert(p.x == 0); - alter_struct_1(pp); //alter_struct_1(&p) is not supported yet + alter_struct_1(pp); assert(p.x == 1 && p.y == 1); struct point p1, p2, p3; @@ -164,10 +162,6 @@ int main(){ struct polygon pol, *ppols; ppols = &pol; pol.ps = ps; - //@ assert Perm(&ppols->ps[0], write); - //@ assert Perm(&ppols->ps[1], write); - //@ assert Perm(&ppols->ps[2], write); - //@ assert (\forall* int i; 0<=i && i<3; Perm(&ppols->ps[i], write)); int avr_pol = avr_x_pol(ppols, 3); // assert sum_seq(inp_to_seq(ppols->ps, 3)) == 6; assert(avr_pol == 2); diff --git a/src/rewrite/vct/rewrite/EncodeArrayValues.scala b/src/rewrite/vct/rewrite/EncodeArrayValues.scala index a3d7757469..52435b4a68 100644 --- a/src/rewrite/vct/rewrite/EncodeArrayValues.scala +++ b/src/rewrite/vct/rewrite/EncodeArrayValues.scala @@ -193,7 +193,7 @@ case class EncodeArrayValues[Pre <: Generation]() extends Rewriter[Pre] { // If structure contains structs, the permission for those fields need to be released as well val permFields = t match { -// case t: TClass[Post] => unwrapStructPerm(access, t, o, makeStruct) + case t: TClass[Post] => unwrapStructPerm(access, t, o, makeStruct) case _ => Seq() } requiresT = diff --git a/src/rewrite/vct/rewrite/PrepareByValueClass.scala b/src/rewrite/vct/rewrite/PrepareByValueClass.scala index 133ad89ec3..06e1eac550 100644 --- a/src/rewrite/vct/rewrite/PrepareByValueClass.scala +++ b/src/rewrite/vct/rewrite/PrepareByValueClass.scala @@ -64,6 +64,8 @@ case object PrepareByValueClass extends RewriterBuilder { private case class InAssignmentStatement(assignment: Assign[_]) extends CopyContext + private case class NoCopy() extends CopyContext + case class PointerLocationDerefBlame(blame: Blame[PointerLocationError]) extends Blame[PointerDerefError] { override def blame(error: PointerDerefError): Unit = { @@ -107,9 +109,11 @@ case class PrepareByValueClass[Pre <: Generation]() extends Rewriter[Pre] { } case assign: Assign[Pre] => { val target = inAssignment.having(()) { dispatch(assign.target) } - copyContext.having(InAssignmentStatement(assign)) { - assign.rewrite(target = target) - } + if (assign.target.t.isInstanceOf[TByValueClass[Pre]]) { + copyContext.having(InAssignmentStatement(assign)) { + assign.rewrite(target = target) + } + } else { assign.rewrite(target = target) } } case _ => node.rewriteDefault() } @@ -277,11 +281,20 @@ case class PrepareByValueClass[Pre <: Generation]() extends Rewriter[Pre] { // unwrapClassPerm(DerefPointer(dispatch(e))(PointerLocationDerefBlame(loc.blame))(loc.o), dispatch(p), e.t.asPointer.get.element.asInstanceOf[TByValueClass[Pre]]) case assign: PreAssignExpression[Pre] => val target = inAssignment.having(()) { dispatch(assign.target) } - copyContext.having(InAssignmentExpression(assign)) { - assign.rewrite(target = target) + if (assign.target.t.isInstanceOf[TByValueClass[Pre]]) { + copyContext.having(InAssignmentExpression(assign)) { + assign.rewrite(target = target) + } + } else { + // No need for copy semantics in this context + copyContext.having(NoCopy()) { assign.rewrite(target = target) } } case invocation: Invocation[Pre] => { - copyContext.having(InCall(invocation)) { invocation.rewriteDefault() } + invocation.rewrite(args = invocation.args.map { a => + if (a.t.isInstanceOf[TByValueClass[Pre]]) { + copyContext.having(InCall(invocation)) { dispatch(a) } + } else { copyContext.having(NoCopy()) { dispatch(a) } } + }) } // WHOOPSIE WE ALSO MAKE A COPY IF IT WAS A POINTER case dp @ DerefPointer(HeapLocal(Ref(v))) @@ -342,6 +355,7 @@ case class PrepareByValueClass[Pre <: Generation]() extends Rewriter[Pre] { t, f => ClassCopyInAssignmentFailed(dp.blame, assignment, clazz, f), ) + case NoCopy() => dp.rewriteDefault() } } } From be64b272edd2174e3d6ee5431da4e88b2b04c635 Mon Sep 17 00:00:00 2001 From: Alexander Stekelenburg Date: Tue, 18 Jun 2024 17:14:04 +0200 Subject: [PATCH 05/26] Enable use of methods on by-value classes --- src/rewrite/vct/rewrite/ClassToRef.scala | 11 +- .../vct/rewrite/lang/LangCPPToCol.scala | 218 +++++++++++------- 2 files changed, 139 insertions(+), 90 deletions(-) diff --git a/src/rewrite/vct/rewrite/ClassToRef.scala b/src/rewrite/vct/rewrite/ClassToRef.scala index bebe3d263e..bdf51b9e52 100644 --- a/src/rewrite/vct/rewrite/ClassToRef.scala +++ b/src/rewrite/vct/rewrite/ClassToRef.scala @@ -91,7 +91,7 @@ case class ClassToRef[Pre <: Generation]() extends Rewriter[Pre] { def makeTypeOf: Function[Post] = { implicit val o: Origin = TypeOfOrigin - val obj = new Variable[Post](TRef()) + val obj = new Variable[Post](TAnyValue()) withResult((result: Result[Post]) => function( blame = AbstractApplicable, @@ -175,10 +175,11 @@ case class ClassToRef[Pre <: Generation]() extends Rewriter[Pre] { ) typeNumber(cls) + val thisType = dispatch(cls.classType(Nil)) cls.decls.foreach { case function: InstanceFunction[Pre] => implicit val o: Origin = function.o - val thisVar = new Variable[Post](TRef())(This) + val thisVar = new Variable[Post](thisType)(This) diz.having(thisVar.get) { functionSucc(function) = globalDeclarations .declare(labelDecls.scope { @@ -213,7 +214,7 @@ case class ClassToRef[Pre <: Generation]() extends Rewriter[Pre] { } case method: InstanceMethod[Pre] => implicit val o: Origin = method.o - val thisVar = new Variable[Post](TRef())(This) + val thisVar = new Variable[Post](thisType)(This) diz.having(thisVar.get) { methodSucc(method) = globalDeclarations.declare(labelDecls.scope { new Procedure( @@ -249,7 +250,7 @@ case class ClassToRef[Pre <: Generation]() extends Rewriter[Pre] { } case cons: Constructor[Pre] => implicit val o: Origin = cons.o - val thisVar = new Variable[Post](TRef())(This) + val thisVar = new Variable[Post](thisType)(This) consSucc(cons) = globalDeclarations.declare(labelDecls.scope { new Procedure( returnType = TVoid(), @@ -293,7 +294,7 @@ case class ClassToRef[Pre <: Generation]() extends Rewriter[Pre] { )) }) case predicate: InstancePredicate[Pre] => - val thisVar = new Variable[Post](TRef())(This) + val thisVar = new Variable[Post](thisType)(This) diz.having(thisVar.get(predicate.o)) { predicateSucc(predicate) = globalDeclarations.declare( new Predicate( diff --git a/src/rewrite/vct/rewrite/lang/LangCPPToCol.scala b/src/rewrite/vct/rewrite/lang/LangCPPToCol.scala index debd341db5..d0708ec692 100644 --- a/src/rewrite/vct/rewrite/lang/LangCPPToCol.scala +++ b/src/rewrite/vct/rewrite/lang/LangCPPToCol.scala @@ -738,7 +738,9 @@ case class LangCPPToCol[Pre <: Generation](rw: LangSpecificToCol[Pre]) implicit val o: Origin = f.o Star( Perm[Post](FieldLocation[Post](thisObj, f.ref), ReadPerm()), - Deref[Post](thisObj, f.ref)(new SYCLRangeDerefBlame(f)) >= c_const(0), + DerefPointer(Deref[Post](thisObj, f.ref)(new SYCLRangeDerefBlame(f)))( + NonNullPointerNull + ) >= c_const(0), ) }) } @@ -763,9 +765,9 @@ case class LangCPPToCol[Pre <: Generation](rw: LangSpecificToCol[Pre]) implicit val o: Origin = params(i).o Star( Perm(FieldLocation[Post](result, rangeFields(i).ref), ReadPerm()), - Deref[Post](result, rangeFields(i).ref)(new SYCLRangeDerefBlame( - rangeFields(i) - )) === Local[Post](params(i).ref), + DerefPointer(Deref[Post](result, rangeFields(i).ref)( + new SYCLRangeDerefBlame(rangeFields(i)) + ))(NonNullPointerNull) === Local[Post](params(i).ref), ) }) } @@ -799,21 +801,23 @@ case class LangCPPToCol[Pre <: Generation](rw: LangSpecificToCol[Pre]) implicit val o: Origin = params(i).o foldStar(Seq( Perm(FieldLocation[Post](result, rangeFields(i).ref), ReadPerm()), - Deref[Post](result, rangeFields(i).ref)(new SYCLRangeDerefBlame( - rangeFields(i) - )) === FloorDiv( + DerefPointer(Deref[Post](result, rangeFields(i).ref)( + new SYCLRangeDerefBlame(rangeFields(i)) + ))(NonNullPointerNull) === FloorDiv( Local[Post](params(i).ref), Local[Post](params(i + 1).ref), )(ImpossibleDivByZeroBlame()), Perm(FieldLocation[Post](result, rangeFields(i + 1).ref), ReadPerm()), - Deref[Post](result, rangeFields(i + 1).ref)(new SYCLRangeDerefBlame( - rangeFields(i + 1) - )) === Local[Post](params(i + 1).ref), - Deref[Post](result, rangeFields(i).ref)(new SYCLRangeDerefBlame( - rangeFields(i) - )) * Deref[Post](result, rangeFields(i + 1).ref)( + DerefPointer(Deref[Post](result, rangeFields(i + 1).ref)( new SYCLRangeDerefBlame(rangeFields(i + 1)) - ) === Local[Post](params(i).ref), + ))(NonNullPointerNull) === Local[Post](params(i + 1).ref), + DerefPointer(Deref[Post](result, rangeFields(i).ref)( + new SYCLRangeDerefBlame(rangeFields(i)) + ))(NonNullPointerNull) * DerefPointer( + Deref[Post](result, rangeFields(i + 1).ref)(new SYCLRangeDerefBlame( + rangeFields(i + 1) + )) + )(NonNullPointerNull) === Local[Post](params(i).ref), )) }) } @@ -1094,9 +1098,11 @@ case class LangCPPToCol[Pre <: Generation](rw: LangSpecificToCol[Pre]) case _: SYCLTAccessor[Pre] => // Referencing an accessor variable can only be done in kernels, otherwise an error will already have been thrown val accessor = syclAccessorSuccessor(ref) - Deref[Post](currentThis.get, accessor.instanceField.ref)( - SYCLAccessorFieldInsufficientReferencePermissionBlame(local) - ) + DerefPointer( + Deref[Post](currentThis.get, accessor.instanceField.ref)( + SYCLAccessorFieldInsufficientReferencePermissionBlame(local) + ) + )(NonNullPointerNull) case _: SYCLTLocalAccessor[Pre] if currentKernelType.get.isInstanceOf[BasicKernel] => throw SYCLNoLocalAccessorsInBasicKernel(local) @@ -1234,18 +1240,18 @@ case class LangCPPToCol[Pre <: Generation](rw: LangSpecificToCol[Pre]) getGlobalWorkItemLinearId(inv) case "sycl::accessor::get_range" => classInstance match { - case Some(Deref(_, ref)) => + case Some(DerefPointer(Deref(_, ref))) => val accessor = syclAccessorSuccessor.values .find(acc => ref.decl.equals(acc.instanceField)).get LiteralSeq[Post]( TCInt(), accessor.rangeIndexFields.map(f => - Deref[Post](currentThis.get, f.ref)( + DerefPointer(Deref[Post](currentThis.get, f.ref)( SYCLAccessorRangeIndexFieldInsufficientReferencePermissionBlame( inv ) - ) + ))(NonNullPointerNull) ), ) case _ => throw NotApplicable(inv) @@ -1567,14 +1573,14 @@ case class LangCPPToCol[Pre <: Generation](rw: LangSpecificToCol[Pre]) val rangeFields: mutable.Buffer[InstanceField[Post]] = mutable.Buffer.empty range.indices.foreach(index => { implicit val o: Origin = range(index).o.where(name = s"range$index") - val instanceField = new InstanceField[Post](TCInt(), Nil) + val instanceField = new InstanceField[Post](TNonNullPointer(TCInt()), Nil) rangeFields.append(instanceField) val iterVar = createRangeIterVar( GlobalScope(), index, - Deref[Post](currentThis.get, instanceField.ref)(new SYCLRangeDerefBlame( - instanceField - )), + DerefPointer(Deref[Post](currentThis.get, instanceField.ref)( + new SYCLRangeDerefBlame(instanceField) + ))(NonNullPointerNull), ) currentDimensionIterVars(GlobalScope()).append(iterVar) }) @@ -1643,28 +1649,30 @@ case class LangCPPToCol[Pre <: Generation](rw: LangSpecificToCol[Pre]) { implicit val o: Origin = kernelDimensions.o .where(name = s"group_range$index") - val groupInstanceField = new InstanceField[Post](TCInt(), Nil) + val groupInstanceField = + new InstanceField[Post](TNonNullPointer(TCInt()), Nil) rangeFields.append(groupInstanceField) val groupIterVar = createRangeIterVar( GroupScope(), index, - Deref[Post](currentThis.get, groupInstanceField.ref)( + DerefPointer(Deref[Post](currentThis.get, groupInstanceField.ref)( new SYCLRangeDerefBlame(groupInstanceField) - ), + ))(NonNullPointerNull), ) currentDimensionIterVars(GroupScope()).append(groupIterVar) } { implicit val o: Origin = localRange(index).o .where(name = s"local_range$index") - val localInstanceField = new InstanceField[Post](TCInt(), Nil) + val localInstanceField = + new InstanceField[Post](TNonNullPointer(TCInt()), Nil) rangeFields.append(localInstanceField) val localIterVar = createRangeIterVar( LocalScope(), index, - Deref[Post](currentThis.get, localInstanceField.ref)( + DerefPointer(Deref[Post](currentThis.get, localInstanceField.ref)( new SYCLRangeDerefBlame(localInstanceField) - ), + ))(NonNullPointerNull), ) currentDimensionIterVars(LocalScope()).append(localIterVar) } @@ -1856,10 +1864,13 @@ case class LangCPPToCol[Pre <: Generation](rw: LangSpecificToCol[Pre]) case None => // No accessor for buffer exist in the command group, so make fields and permissions val instanceField = - new InstanceField[Post](buffer.generatedVar.t, Nil)(accO) + new InstanceField[Post]( + TNonNullPointer(buffer.generatedVar.t), + Nil, + )(accO) val rangeIndexFields = Seq .range(0, buffer.range.dimensions.size).map(i => - new InstanceField[Post](TCInt(), Nil)( + new InstanceField[Post](TNonNullPointer(TCInt()), Nil)( dimO.where(name = s"${accName}_r$i") ) ) @@ -1931,7 +1942,11 @@ case class LangCPPToCol[Pre <: Generation](rw: LangSpecificToCol[Pre]) } val rangeIndexDerefs: Seq[Expr[Post]] = acc.rangeIndexFields.map(f => - Deref[Post](classObj, f.ref)(new SYCLAccessorDimensionDerefBlame(f))(f.o) + DerefPointer( + Deref[Post](classObj, f.ref)(new SYCLAccessorDimensionDerefBlame(f))( + f.o + ) + )(NonNullPointerNull)(f.o) ) ( @@ -1949,18 +1964,22 @@ case class LangCPPToCol[Pre <: Generation](rw: LangSpecificToCol[Pre]) ReadPerm()(acc.instanceField.o), )(acc.instanceField.o), ValidArray( - Deref[Post](classObj, acc.instanceField.ref)( - new SYCLAccessorDerefBlame(acc.instanceField) - )(acc.instanceField.o), + DerefPointer( + Deref[Post](classObj, acc.instanceField.ref)( + new SYCLAccessorDerefBlame(acc.instanceField) + )(acc.instanceField.o) + )(NonNullPointerNull)(acc.instanceField.o), rangeIndexDerefs.reduce((e1, e2) => (e1 * e2)(acc.buffer.o)), )(acc.instanceField.o), ) )(acc.instanceField.o), Perm( ArrayLocation( - Deref[Post](classObj, acc.instanceField.ref)( - new SYCLAccessorDerefBlame(acc.instanceField) - )(acc.instanceField.o), + DerefPointer( + Deref[Post](classObj, acc.instanceField.ref)( + new SYCLAccessorDerefBlame(acc.instanceField) + )(acc.instanceField.o) + )(NonNullPointerNull)(acc.instanceField.o), Any()(PanicBlame( "The accessor field is not null as that was proven in the previous conditions." ))(acc.instanceField.o), @@ -2012,20 +2031,24 @@ case class LangCPPToCol[Pre <: Generation](rw: LangSpecificToCol[Pre]) constructorPostConditions.append( foldStar[Post]( Eq[Post]( - Deref[Post](result, acc.instanceField.ref)( - new SYCLAccessorDerefBlame(acc.instanceField) - )(acc.instanceField.o), + DerefPointer( + Deref[Post](result, acc.instanceField.ref)( + new SYCLAccessorDerefBlame(acc.instanceField) + )(acc.instanceField.o) + )(NonNullPointerNull)(acc.instanceField.o), Local[Post](newConstructorAccessorArg.ref)( newConstructorAccessorArg.o ), )(newConstructorAccessorArg.o) +: Seq.range(0, acc.rangeIndexFields.size).map(i => Eq[Post]( - Deref[Post](result, acc.rangeIndexFields(i).ref)( - new SYCLAccessorDimensionDerefBlame( - acc.rangeIndexFields(i) - ) - )(acc.rangeIndexFields(i).o), + DerefPointer( + Deref[Post](result, acc.rangeIndexFields(i).ref)( + new SYCLAccessorDimensionDerefBlame( + acc.rangeIndexFields(i) + ) + )(acc.rangeIndexFields(i).o) + )(NonNullPointerNull)(acc.rangeIndexFields(i).o), Local[Post](newConstructorAccessorDimensionArgs(i).ref)( newConstructorAccessorDimensionArgs(i).o ), @@ -2483,12 +2506,14 @@ case class LangCPPToCol[Pre <: Generation](rw: LangSpecificToCol[Pre]) CPP.unwrappedType(base.t) match { case SYCLTAccessor(_, 1, _) => ArraySubscript[Post]( - Deref[Post]( - currentThis.get, - syclAccessorSuccessor(base.ref.get).instanceField.ref, - )(new SYCLAccessorDerefBlame( - syclAccessorSuccessor(base.ref.get).instanceField - ))(sub.o), + DerefPointer( + Deref[Post]( + currentThis.get, + syclAccessorSuccessor(base.ref.get).instanceField.ref, + )(new SYCLAccessorDerefBlame( + syclAccessorSuccessor(base.ref.get).instanceField + ))(sub.o) + )(NonNullPointerNull)(sub.o), rw.dispatch(index), )(SYCLAccessorArraySubscriptErrorBlame(sub))(sub.o) case t: SYCLTAccessor[Pre] => @@ -2504,19 +2529,25 @@ case class LangCPPToCol[Pre <: Generation](rw: LangSpecificToCol[Pre]) val linearizeArgs = Seq( rw.dispatch(indexX), rw.dispatch(indexY), - Deref[Post](currentThis.get, accessor.rangeIndexFields(0).ref)( - new SYCLAccessorDimensionDerefBlame(accessor.rangeIndexFields(0)) - ), - Deref[Post](currentThis.get, accessor.rangeIndexFields(1).ref)( - new SYCLAccessorDimensionDerefBlame(accessor.rangeIndexFields(1)) - ), + DerefPointer( + Deref[Post](currentThis.get, accessor.rangeIndexFields(0).ref)( + new SYCLAccessorDimensionDerefBlame(accessor.rangeIndexFields(0)) + ) + )(NonNullPointerNull), + DerefPointer( + Deref[Post](currentThis.get, accessor.rangeIndexFields(1).ref)( + new SYCLAccessorDimensionDerefBlame(accessor.rangeIndexFields(1)) + ) + )(NonNullPointerNull), ) CPP.unwrappedType(base.t) match { case SYCLTAccessor(_, 2, _) => ArraySubscript[Post]( - Deref[Post](currentThis.get, accessor.instanceField.ref)( - new SYCLAccessorDerefBlame(accessor.instanceField) - ), + DerefPointer( + Deref[Post](currentThis.get, accessor.instanceField.ref)( + new SYCLAccessorDerefBlame(accessor.instanceField) + ) + )(NonNullPointerNull), syclHelperFunctions("sycl_:_:linearize_2")( linearizeArgs, SYCLAccessorArraySubscriptLinearizeInvocationBlame( @@ -2544,22 +2575,30 @@ case class LangCPPToCol[Pre <: Generation](rw: LangSpecificToCol[Pre]) rw.dispatch(indexX), rw.dispatch(indexY), rw.dispatch(indexZ), - Deref[Post](currentThis.get, accessor.rangeIndexFields(0).ref)( - new SYCLAccessorDimensionDerefBlame(accessor.rangeIndexFields(0)) - ), - Deref[Post](currentThis.get, accessor.rangeIndexFields(1).ref)( - new SYCLAccessorDimensionDerefBlame(accessor.rangeIndexFields(1)) - ), - Deref[Post](currentThis.get, accessor.rangeIndexFields(2).ref)( - new SYCLAccessorDimensionDerefBlame(accessor.rangeIndexFields(2)) - ), + DerefPointer( + Deref[Post](currentThis.get, accessor.rangeIndexFields(0).ref)( + new SYCLAccessorDimensionDerefBlame(accessor.rangeIndexFields(0)) + ) + )(NonNullPointerNull), + DerefPointer( + Deref[Post](currentThis.get, accessor.rangeIndexFields(1).ref)( + new SYCLAccessorDimensionDerefBlame(accessor.rangeIndexFields(1)) + ) + )(NonNullPointerNull), + DerefPointer( + Deref[Post](currentThis.get, accessor.rangeIndexFields(2).ref)( + new SYCLAccessorDimensionDerefBlame(accessor.rangeIndexFields(2)) + ) + )(NonNullPointerNull), ) CPP.unwrappedType(base.t) match { case SYCLTAccessor(_, 3, _) => ArraySubscript[Post]( - Deref[Post](currentThis.get, accessor.instanceField.ref)( - new SYCLAccessorDerefBlame(accessor.instanceField) - ), + DerefPointer( + Deref[Post](currentThis.get, accessor.instanceField.ref)( + new SYCLAccessorDerefBlame(accessor.instanceField) + ) + )(NonNullPointerNull), syclHelperFunctions("sycl_:_:linearize_3")( linearizeArgs, SYCLAccessorArraySubscriptLinearizeInvocationBlame( @@ -2637,9 +2676,12 @@ case class LangCPPToCol[Pre <: Generation](rw: LangSpecificToCol[Pre]) removeKernelClassInstancePermissions(right), ) - case ActionPerm(Deref(obj, _), _) if obj.equals(this.currentThis.get) => + case ActionPerm(DerefPointer(Deref(obj, _)), _) + if obj.equals(this.currentThis.get) => + tt + case ModelPerm(DerefPointer(Deref(obj, _)), _) + if obj.equals(this.currentThis.get) => tt - case ModelPerm(Deref(obj, _), _) if obj.equals(this.currentThis.get) => tt case Perm(FieldLocation(obj, _), _) if obj.equals(this.currentThis.get) => tt case PointsTo(FieldLocation(obj, _), _, _) @@ -2647,14 +2689,20 @@ case class LangCPPToCol[Pre <: Generation](rw: LangSpecificToCol[Pre]) tt case Value(FieldLocation(obj, _)) if obj.equals(this.currentThis.get) => tt - case Perm(AmbiguousLocation(ArraySubscript(Deref(obj, _), _)), _) - if obj.equals(this.currentThis.get) => + case Perm( + AmbiguousLocation(ArraySubscript(DerefPointer(Deref(obj, _)), _)), + _, + ) if obj.equals(this.currentThis.get) => tt - case PointsTo(AmbiguousLocation(ArraySubscript(Deref(obj, _), _)), _, _) - if obj.equals(this.currentThis.get) => + case PointsTo( + AmbiguousLocation(ArraySubscript(DerefPointer(Deref(obj, _)), _)), + _, + _, + ) if obj.equals(this.currentThis.get) => tt - case Value(AmbiguousLocation(ArraySubscript(Deref(obj, _), _))) - if obj.equals(this.currentThis.get) => + case Value( + AmbiguousLocation(ArraySubscript(DerefPointer(Deref(obj, _)), _)) + ) if obj.equals(this.currentThis.get) => tt case Implies(left, right) => Implies( @@ -2685,9 +2733,9 @@ case class LangCPPToCol[Pre <: Generation](rw: LangSpecificToCol[Pre]) case SYCLAccessor(buffer, SYCLReadWriteAccess(), instanceField, _) => assignLocal( buffer.generatedVar.get, - Deref[Post](variable, instanceField.ref)(new SYCLAccessorDerefBlame( - instanceField - )), + DerefPointer(Deref[Post](variable, instanceField.ref)( + new SYCLAccessorDerefBlame(instanceField) + ))(NonNullPointerNull), ) })) } From aed3ba6c3295021cd0d24b13ad7d4d65a53dd958 Mon Sep 17 00:00:00 2001 From: Alexander Stekelenburg Date: Wed, 19 Jun 2024 11:01:30 +0200 Subject: [PATCH 06/26] Set --useOldAxiomatization to test which test failures are because of me and which aren't --- src/viper/viper/api/backend/silicon/Silicon.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/src/viper/viper/api/backend/silicon/Silicon.scala b/src/viper/viper/api/backend/silicon/Silicon.scala index 0fd0ab0b6e..aa1244309f 100644 --- a/src/viper/viper/api/backend/silicon/Silicon.scala +++ b/src/viper/viper/api/backend/silicon/Silicon.scala @@ -97,6 +97,7 @@ case class Silicon( "--z3ConfigArgs", z3Config, "--ideModeAdvanced", + "--useOldAxiomatization", ) if (proverLogFile.isDefined) { From 53f19a8309d13882c2cd7495f0a95c0152fe898a Mon Sep 17 00:00:00 2001 From: Alexander Stekelenburg Date: Thu, 18 Jul 2024 14:40:29 +0200 Subject: [PATCH 07/26] Improve struct encoding, rewrite tests for new permission syntax --- examples/concepts/c/structs.c | 4 +- res/universal/res/adt/pointer.pvl | 7 - src/col/vct/col/typerules/CoercionUtils.scala | 3 + src/rewrite/vct/rewrite/ClassToRef.scala | 202 +++++++++++++----- .../vct/rewrite/PrepareByValueClass.scala | 103 ++++++--- .../vct/rewrite/adt/ImportOption.scala | 13 ++ .../vct/rewrite/adt/ImportPointer.scala | 60 +++++- .../viper/api/backend/SilverBackend.scala | 2 +- .../viper/api/backend/silicon/Silicon.scala | 1 - .../viper/api/transform/ColToSilver.scala | 14 +- .../vct/test/integration/examples/CSpec.scala | 14 +- 11 files changed, 320 insertions(+), 103 deletions(-) diff --git a/examples/concepts/c/structs.c b/examples/concepts/c/structs.c index 960a6fa1ca..4638fd1ac4 100644 --- a/examples/concepts/c/structs.c +++ b/examples/concepts/c/structs.c @@ -52,6 +52,7 @@ void alter_copy_struct(struct point p){ p.y = 0; } +// TODO: Should be auto-generated /*@ context Perm(p, 1\1); @*/ @@ -132,6 +133,7 @@ int main(){ struct point *pp; pp = &p; + /* //@ assert (pp[0] != NULL ); */ assert (pp != NULL ); p.x = 1; @@ -145,7 +147,7 @@ int main(){ alter_struct(pp); assert(pp->x == 0); assert(p.x == 0); - alter_struct_1(pp); + alter_struct_1(pp); //alter_struct_1(&p) is not supported yet assert(p.x == 1 && p.y == 1); struct point p1, p2, p3; diff --git a/res/universal/res/adt/pointer.pvl b/res/universal/res/adt/pointer.pvl index 6c4da7e5db..9cbbedb16b 100644 --- a/res/universal/res/adt/pointer.pvl +++ b/res/universal/res/adt/pointer.pvl @@ -31,13 +31,6 @@ adt `pointer` { axiom (∀ ref r; ptr_deref({:pointer_inv(r):}) == r); axiom (∀ `pointer` p; pointer_inv({:ptr_deref(p):}) == p); - - axiom (∀ `pointer` p1, `pointer` p2, int offset; - (0 <= offset && offset < `block`.block_length(pointer_block(p1)) && - pointer_block(p1) == pointer_block(p2) && - {:pointer_of(pointer_block(p1), offset):} == - {:pointer_of(pointer_block(p2), offset):}) ==> p1 == p2 - ); } decreases; diff --git a/src/col/vct/col/typerules/CoercionUtils.scala b/src/col/vct/col/typerules/CoercionUtils.scala index a71e109e01..5c16be6dd6 100644 --- a/src/col/vct/col/typerules/CoercionUtils.scala +++ b/src/col/vct/col/typerules/CoercionUtils.scala @@ -143,6 +143,9 @@ case object CoercionUtils { case (TNonNullPointer(innerType), TPointer(element)) if innerType == element => CoerceNonNullPointer(innerType) + case (TNonNullPointer(a), TNonNullPointer(b)) + if getAnyCoercion(a, b).isDefined => + CoerceIdentity(target) case ( TPointer(element), CTPointer(innerType), diff --git a/src/rewrite/vct/rewrite/ClassToRef.scala b/src/rewrite/vct/rewrite/ClassToRef.scala index bdf51b9e52..aa4c140937 100644 --- a/src/rewrite/vct/rewrite/ClassToRef.scala +++ b/src/rewrite/vct/rewrite/ClassToRef.scala @@ -321,7 +321,7 @@ case class ClassToRef[Pre <: Generation]() extends Rewriter[Pre] { case cls: ByValueClass[Pre] => implicit val o: Origin = cls.o val axiomType = TAxiomatic[Post](byValClassSucc.ref(cls), Nil) - val (fieldFunctions, fieldTypes) = + val (fieldFunctions, fieldInverses, fieldTypes) = cls.decls.collect { case field: Field[Pre] => val newT = dispatch(field.t) byValFieldSucc(field) = @@ -329,27 +329,73 @@ case class ClassToRef[Pre <: Generation]() extends Rewriter[Pre] { Seq(new Variable(axiomType)(field.o)), newT, )(field.o) - (byValFieldSucc(field), newT) - }.unzip + ( + byValFieldSucc(field), + new ADTFunction[Post]( + Seq(new Variable(newT)(field.o)), + axiomType, + )( + field.o.copy( + field.o.originContents + .filterNot(_.isInstanceOf[SourceName]) + ).where(name = + "inv_" + field.o.find[SourceName].map(_.name) + .getOrElse("unknown") + ) + ), + newT, + ) + }.unzip3 val constructor = - new ADTFunction[Post](fieldTypes.map(new Variable(_)), axiomType)( - cls.o + new ADTFunction[Post]( + fieldTypes.zipWithIndex.map { case (t, i) => + new Variable(t)(Origin(Seq( + PreferredName(Seq("p_" + i)), + LabelContext("classToRef"), + ))) + }, + axiomType, + )( + cls.o.copy( + cls.o.originContents.filterNot(_.isInstanceOf[SourceName]) + ).where(name = + "new_" + cls.o.find[SourceName].map(_.name) + .getOrElse("unknown") + ) + ) + // TAnyValue is a placeholder the pointer adt doesn't have type parameters + val indexFunction = + new ADTFunction[Post]( + Seq(new Variable(TNonNullPointer(TAnyValue()))(Origin( + Seq(PreferredName(Seq("pointer")), LabelContext("classToRef")) + ))), + TInt(), + )( + cls.o.copy( + cls.o.originContents.filterNot(_.isInstanceOf[SourceName]) + ).where(name = + "index_" + cls.o.find[SourceName].map(_.name) + .getOrElse("unknown") + ) ) val destructorAxiom = new ADTAxiom[Post](foralls( fieldTypes, body = variables => { - foldAnd(variables.zip(fieldFunctions).map { case (v, f) => - adtFunctionInvocation[Post]( - f.ref, - args = Seq(adtFunctionInvocation[Post]( - constructor.ref, - None, - args = variables, - )), - ) === v - }) + foldAnd(variables.combinations(2).map { case Seq(v1, v2) => + v1 !== v2 + }.toSeq) ==> + foldAnd(variables.zip(fieldFunctions).map { case (v, f) => + adtFunctionInvocation[Post]( + f.ref, + args = Seq(adtFunctionInvocation[Post]( + constructor.ref, + None, + args = variables, + )), + ) === v + }) }, triggers = variables => { @@ -365,6 +411,8 @@ case class ClassToRef[Pre <: Generation]() extends Rewriter[Pre] { } }, )) + + // This one generates a matching loop val injectivityAxiom1 = new ADTAxiom[Post](foralls( Seq(axiomType, axiomType), @@ -409,15 +457,56 @@ case class ClassToRef[Pre <: Generation]() extends Rewriter[Pre] { } }, )) + val destructorAxioms = fieldFunctions.zip(fieldInverses).map { + case (f, inv) => + new ADTAxiom[Post](forall( + axiomType, + body = { a => + adtFunctionInvocation[Post]( + inv.ref, + None, + args = Seq( + adtFunctionInvocation[Post](f.ref, None, args = Seq(a)) + ), + ) === a + }, + triggers = { a => + Seq(Seq( + adtFunctionInvocation[Post](f.ref, None, args = Seq(a)) + )) + }, + )) + } + val indexAxioms = fieldFunctions.zipWithIndex.map { case (f, i) => + new ADTAxiom[Post](forall( + axiomType, + body = { a => + adtFunctionInvocation[Post]( + indexFunction.ref, + None, + args = Seq( + adtFunctionInvocation[Post](f.ref, None, args = Seq(a)) + ), + ) === const(i) + }, + triggers = { a => + Seq( + Seq(adtFunctionInvocation[Post](f.ref, None, args = Seq(a))) + ) + }, + )) + } byValConsSucc(cls) = constructor byValClassSucc(cls) = new AxiomaticDataType[Post]( Seq( - constructor, - destructorAxiom, - injectivityAxiom1, +// constructor, +// destructorAxiom, + indexFunction, +// injectivityAxiom1, injectivityAxiom2, - ) ++ fieldFunctions, + ) ++ destructorAxioms ++ indexAxioms ++ fieldFunctions ++ + fieldInverses, Nil, ) globalDeclarations.succeed(cls, byValClassSucc(cls)) @@ -448,41 +537,46 @@ case class ClassToRef[Pre <: Generation]() extends Rewriter[Pre] { )(PanicBlame("typeOf requires nothing.")) === const(typeNumber(cls)) ), )) - case cls: ByValueClass[Pre] => - val (assigns, vars) = - cls.decls.collect { case field: InstanceField[Pre] => - val element = field.t.asPointer.get.element - val newE = dispatch(element) - val v = new Variable[Post](TNonNullPointer(newE)) - ( - InvokeProcedure[Post]( - pointerCreationMethods - .getOrElseUpdate(element, makePointerCreationMethod(newE)) - .ref, - Nil, - Seq(v.get), - Nil, - Nil, - Nil, - )(TrueSatisfiable), - v, - ) - }.unzip - Scope( - vars, - Block( - assigns ++ Seq( - Assign( - Local(target), - adtFunctionInvocation[Post]( - byValConsSucc.ref(cls), - args = vars.map(_.get), - ), - )(AssignLocalOk) - // TODO: Add back typeOf here (but use a separate definition for the adt) - ) - ), - ) + case cls: ByValueClass[Pre] => throw ExtraNode +// val (assigns, vars) = +// cls.decls.collect { case field: InstanceField[Pre] => +// val element = field.t.asPointer.get.element +// val newE = dispatch(element) +// val v = new Variable[Post](TNonNullPointer(newE)) +// ( +// InvokeProcedure[Post]( +// pointerCreationMethods +// .getOrElseUpdate(element, makePointerCreationMethod(newE)) +// .ref, +// Nil, +// Seq(v.get), +// Nil, +// Nil, +// Nil, +// )(TrueSatisfiable), +// v, +// ) +// }.unzip +// val assertions = if (vars.size > 1) { +// Seq(Assert(foldAnd[Post](vars.combinations(2).map { case Seq(a,b) => a.get !== b.get}.toSeq))(PanicBlame("Newly created pointers should be distinct"))) +// } else { +// Nil +// } +// Scope( +// vars, +// Block( +// assigns ++ assertions ++ Seq( +// Assign( +// Local(target), +// adtFunctionInvocation[Post]( +// byValConsSucc.ref(cls), +// args = vars.map(_.get), +// ), +// )(AssignLocalOk) +// // TODO: Add back typeOf here (but use a separate definition for the adt) +// ) +// ), +// ) } } diff --git a/src/rewrite/vct/rewrite/PrepareByValueClass.scala b/src/rewrite/vct/rewrite/PrepareByValueClass.scala index 06e1eac550..fdfaa81458 100644 --- a/src/rewrite/vct/rewrite/PrepareByValueClass.scala +++ b/src/rewrite/vct/rewrite/PrepareByValueClass.scala @@ -7,6 +7,7 @@ import vct.col.ref.Ref import vct.col.resolve.ctx.Referrable import vct.col.rewrite.{Generation, Rewriter, RewriterBuilder} import vct.col.util.AstBuildHelpers._ +import vct.col.util.SuccessionMap import vct.result.VerificationError.{Unreachable, UserError} // TODO: Think of a better name @@ -86,14 +87,32 @@ case class PrepareByValueClass[Pre <: Generation]() extends Rewriter[Pre] { private val inAssignment: ScopedStack[Unit] = ScopedStack() private val copyContext: ScopedStack[CopyContext] = ScopedStack() + private val classCreationMethods + : SuccessionMap[TByValueClass[Pre], Procedure[Post]] = SuccessionMap() + + def makeClassCreationMethod(t: TByValueClass[Pre]): Procedure[Post] = { + implicit val o: Origin = t.cls.decl.o + + globalDeclarations.declare(withResult((result: Result[Post]) => + procedure[Post]( + blame = AbstractApplicable, + contractBlame = TrueSatisfiable, + returnType = dispatch(t), + ensures = UnitAccountedPredicate( + unwrapClassPerm(result, WritePerm(), t) + ), + decreases = Some(DecreasesClauseNoRecursion[Post]()), + ) + )) + } override def dispatch(node: Statement[Pre]): Statement[Post] = { implicit val o: Origin = node.o node match { case HeapLocalDecl(local) if local.t.asPointer.get.element.isInstanceOf[TByValueClass[Pre]] => { + val t = local.t.asPointer.get.element.asInstanceOf[TByValueClass[Pre]] val newLocal = localHeapVariables.dispatch(local) - val t = newLocal.t.asPointer.get.element Block(Seq( HeapLocalDecl(newLocal), // Assign( @@ -103,17 +122,33 @@ case class PrepareByValueClass[Pre <: Generation]() extends Rewriter[Pre] { // TODO: Only do this if the first use does not overwrite it again (do something similar to what I implemented in ImportPointer).... Assign( newLocal.get(DerefAssignTarget), - NewObject(t.asInstanceOf[TByValueClass[Post]].cls), + procedureInvocation[Post]( + TrueSatisfiable, + classCreationMethods + .getOrElseUpdate(t, makeClassCreationMethod(t)).ref, + ), )(AssignLocalOk), )) } - case assign: Assign[Pre] => { + case assign: Assign[Pre] => val target = inAssignment.having(()) { dispatch(assign.target) } if (assign.target.t.isInstanceOf[TByValueClass[Pre]]) { copyContext.having(InAssignmentStatement(assign)) { assign.rewrite(target = target) } } else { assign.rewrite(target = target) } + case Instantiate(Ref(cls), out) + if cls.isInstanceOf[ByValueClass[Pre]] => { + // AssignLocalOk doesn't make too much sense since we don't know if out is a local + val t = TByValueClass[Pre](cls.ref, Seq()) + Assign[Post]( + dispatch(out), + procedureInvocation( + TrueSatisfiable, + classCreationMethods.getOrElseUpdate(t, makeClassCreationMethod(t)) + .ref, + ), + )(AssignLocalOk) } case _ => node.rewriteDefault() } @@ -125,9 +160,8 @@ case class PrepareByValueClass[Pre <: Generation]() extends Rewriter[Pre] { blame: InstanceField[Pre] => Blame[InsufficientPermission], ): Expr[Post] = { implicit val o: Origin = obj.o - val ov = new Variable[Post](obj.t) - val v = - new Variable[Post](dispatch(t))(o.withContent(TypeName("HelloWorld"))) + val ov = new Variable[Post](obj.t)(o.where(name = "original")) + val v = new Variable[Post](dispatch(t))(o.where(name = "copy")) val children = t.cls.decl.decls.collect { case f: InstanceField[Pre] => f.t match { case inner: TByValueClass[Pre] => @@ -154,9 +188,14 @@ case class PrepareByValueClass[Pre <: Generation]() extends Rewriter[Pre] { Then( With( assignLocal(ov.get, obj), - PreAssignExpression(v.get, NewObject[Post](succ(t.cls.decl)))( - AssignLocalOk - ), + PreAssignExpression( + v.get, + procedureInvocation[Post]( + TrueSatisfiable, + classCreationMethods + .getOrElseUpdate(t, makeClassCreationMethod(t)).ref, + ), + )(AssignLocalOk), ), Block(children), ), @@ -233,26 +272,34 @@ case class PrepareByValueClass[Pre <: Generation]() extends Rewriter[Pre] { override def dispatch(node: Expr[Pre]): Expr[Post] = { implicit val o: Origin = node.o node match { - case Eq(left, right) - if left.t == right.t && left.t.isInstanceOf[TByValueClass[Pre]] => - val newLeft = dispatch(left) - val newRight = dispatch(right) - return Eq(newLeft, newRight) && unwrapClassComp( - (l, r) => Eq(l, r), - newLeft, - newRight, - left.t.asInstanceOf[TByValueClass[Pre]], - ) - case Neq(left, right) - if left.t == right.t && left.t.isInstanceOf[TByValueClass[Pre]] => - val newLeft = dispatch(left) - val newRight = dispatch(right) - return Neq(newLeft, newRight) && unwrapClassComp( - (l, r) => Neq(l, r), - newLeft, - newRight, - left.t.asInstanceOf[TByValueClass[Pre]], + case NewObject(Ref(cls)) if cls.isInstanceOf[ByValueClass[Pre]] => { + val t = TByValueClass[Pre](cls.ref, Seq()) + procedureInvocation[Post]( + TrueSatisfiable, + classCreationMethods.getOrElseUpdate(t, makeClassCreationMethod(t)) + .ref, ) + } +// case Eq(left, right) +// if left.t == right.t && left.t.isInstanceOf[TByValueClass[Pre]] => +// val newLeft = dispatch(left) +// val newRight = dispatch(right) +// return Eq(newLeft, newRight) && unwrapClassComp( +// (l, r) => Eq(l, r), +// newLeft, +// newRight, +// left.t.asInstanceOf[TByValueClass[Pre]], +// ) +// case Neq(left, right) +// if left.t == right.t && left.t.isInstanceOf[TByValueClass[Pre]] => +// val newLeft = dispatch(left) +// val newRight = dispatch(right) +// return Neq(newLeft, newRight) && unwrapClassComp( +// (l, r) => Neq(l, r), +// newLeft, +// newRight, +// left.t.asInstanceOf[TByValueClass[Pre]], +// ) case _ => {} } if (inAssignment.nonEmpty) diff --git a/src/rewrite/vct/rewrite/adt/ImportOption.scala b/src/rewrite/vct/rewrite/adt/ImportOption.scala index 0ac9e05c37..8dd885426d 100644 --- a/src/rewrite/vct/rewrite/adt/ImportOption.scala +++ b/src/rewrite/vct/rewrite/adt/ImportOption.scala @@ -68,6 +68,19 @@ case class ImportOption[Pre <: Generation](importer: ImportADTImporter) case other => rewriteDefault(other) } + override def preCoerce(e: Expr[Pre]): Expr[Pre] = + e match { + case OptGet(OptSome(inner)) => inner + case OptGet(OptSomeTyped(_, inner)) => inner + case OptGetOrElse(OptSome(inner), _) => inner + case OptGetOrElse(OptSomeTyped(_, inner), _) => inner + case OptSomeTyped(t, OptGet(inner)) + if inner.t.asOption.get.element == t => + inner + case OptSome(OptGet(inner)) => inner + case _ => super.preCoerce(e) + } + override def postCoerce(e: Expr[Pre]): Expr[Post] = e match { case OptEmpty(opt) => diff --git a/src/rewrite/vct/rewrite/adt/ImportPointer.scala b/src/rewrite/vct/rewrite/adt/ImportPointer.scala index dfa103b0f1..9c5d253e85 100644 --- a/src/rewrite/vct/rewrite/adt/ImportPointer.scala +++ b/src/rewrite/vct/rewrite/adt/ImportPointer.scala @@ -15,7 +15,7 @@ case object ImportPointer extends ImportADTBuilder("pointer") { Origin(Seq(PreferredName(Seq(typeText(t))), LabelContext("pointer field"))) private val PointerCreationOrigin: Origin = Origin( - Seq(LabelContext("classToRef, pointer creation method")) + Seq(LabelContext("adtPointer, pointer creation method")) ) case class PointerNullOptNone(inner: Blame[PointerNull], expr: Expr[_]) @@ -87,8 +87,10 @@ case class ImportPointer[Pre <: Generation](importer: ImportADTImporter) private def makePointerCreationMethod(t: Type[Post]): Procedure[Post] = { implicit val o: Origin = PointerCreationOrigin + .where(name = "create_nonnull_pointer_" + t.toString) - val result = new Variable[Post](TAxiomatic(pointerAdt.ref, Nil)) + val result = + new Variable[Post](TAxiomatic(pointerAdt.ref, Nil))(o.where(name = "res")) globalDeclarations.declare(procedure[Post]( blame = AbstractApplicable, contractBlame = TrueSatisfiable, @@ -225,9 +227,63 @@ case class ImportPointer[Pre <: Generation](importer: ImportADTImporter) } } + def rewriteTopLevelPointerSubscriptInTrigger(e: Expr[Pre]): Expr[Post] = { + implicit val o: Origin = e.o + e match { + case sub @ PointerSubscript(pointer, index) => + FunctionInvocation[Post]( + ref = pointerDeref.ref, + args = Seq( + FunctionInvocation[Post]( + ref = pointerAdd.ref, + args = Seq(unwrapOption(pointer, sub.blame), dispatch(index)), + typeArgs = Nil, + Nil, + Nil, + )(NoContext(PointerBoundsPreconditionFailed(sub.blame, index))) + ), + typeArgs = Nil, + Nil, + Nil, + )(PanicBlame("ptr_deref requires nothing.")) + case deref @ DerefPointer(pointer) => + FunctionInvocation[Post]( + ref = pointerDeref.ref, + args = Seq( + FunctionInvocation[Post]( + ref = pointerAdd.ref, + // Always index with zero, otherwise quantifiers with pointers do not get triggered + args = Seq(unwrapOption(pointer, deref.blame), const(0)), + typeArgs = Nil, + Nil, + Nil, + )(NoContext( + DerefPointerBoundsPreconditionFailed(deref.blame, pointer) + )) + ), + typeArgs = Nil, + Nil, + Nil, + )(PanicBlame("ptr_deref requires nothing.")) + case other => rewriteDefault(other) + } + } + override def postCoerce(e: Expr[Pre]): Expr[Post] = { implicit val o: Origin = e.o e match { +// case f @ Forall(_, triggers, _) => +// f.rewrite(triggers = +// triggers.map(_.map(rewriteTopLevelPointerSubscriptInTrigger)) +// ) +// case s @ Starall(_, triggers, _) => +// s.rewrite(triggers = +// triggers.map(_.map(rewriteTopLevelPointerSubscriptInTrigger)) +// ) +// case e @ Exists(_, triggers, _) => +// e.rewrite(triggers = +// triggers.map(_.map(rewriteTopLevelPointerSubscriptInTrigger)) +// ) case sub @ PointerSubscript(pointer, index) => SilverDeref( obj = diff --git a/src/viper/viper/api/backend/SilverBackend.scala b/src/viper/viper/api/backend/SilverBackend.scala index 2e1161e85e..c4a3144068 100644 --- a/src/viper/viper/api/backend/SilverBackend.scala +++ b/src/viper/viper/api/backend/SilverBackend.scala @@ -392,7 +392,7 @@ trait SilverBackend .NegativePermissionValue( info(p).permissionValuePermissionNode.get ) // need to fetch access - case _ => ??? + case r => throw new NotImplementedError("Missing: " + r) } def getDecreasesClause(reason: ErrorReason): col.DecreasesClause[_] = diff --git a/src/viper/viper/api/backend/silicon/Silicon.scala b/src/viper/viper/api/backend/silicon/Silicon.scala index aa1244309f..0fd0ab0b6e 100644 --- a/src/viper/viper/api/backend/silicon/Silicon.scala +++ b/src/viper/viper/api/backend/silicon/Silicon.scala @@ -97,7 +97,6 @@ case class Silicon( "--z3ConfigArgs", z3Config, "--ideModeAdvanced", - "--useOldAxiomatization", ) if (proverLogFile.isDefined) { diff --git a/src/viper/viper/api/transform/ColToSilver.scala b/src/viper/viper/api/transform/ColToSilver.scala index 9ca1a94c77..eb9f55580a 100644 --- a/src/viper/viper/api/transform/ColToSilver.scala +++ b/src/viper/viper/api/transform/ColToSilver.scala @@ -7,7 +7,7 @@ import vct.col.ref.Ref import vct.col.util.AstBuildHelpers.unfoldStar import vct.col.{ast => col} import vct.result.VerificationError.{SystemError, Unreachable} -import viper.silver.ast.TypeVar +import viper.silver.ast.{AnnotationInfo, ConsInfo, TypeVar} import viper.silver.plugin.standard.termination.{ DecreasesClause, DecreasesTuple, @@ -231,7 +231,17 @@ case class ColToSilver(program: col.Program[_]) { function.contract.decreases.toSeq.map(decreases), pred(function.contract.ensures), function.body.map(exp), - )(pos = pos(function), info = NodeInfo(function)) + )( + pos = pos(function), + info = + if (ref(function) == "ptrDerefblahblah") + ConsInfo( + AnnotationInfo(Map("opaque" -> Seq())), + NodeInfo(function), + ) + else + NodeInfo(function), + ) } case procedure: col.Procedure[_] if procedure.returnType == col.TVoid() && !procedure.inline && diff --git a/test/main/vct/test/integration/examples/CSpec.scala b/test/main/vct/test/integration/examples/CSpec.scala index b5af488aab..d8209460ad 100644 --- a/test/main/vct/test/integration/examples/CSpec.scala +++ b/test/main/vct/test/integration/examples/CSpec.scala @@ -18,7 +18,7 @@ class CSpec extends VercorsSpec { int x = 4.0 % 1; } """ - vercors should fail withCode "assignFieldFailed" using silicon in "cannot access field of struct after freeing" c + vercors should fail withCode "ptrPerm" using silicon in "cannot access field of struct after freeing" c """ #include @@ -76,7 +76,7 @@ class CSpec extends VercorsSpec { int main(){ struct d* xs = (struct d*) malloc(sizeof(struct d)*3); struct d* ys = (struct d*) malloc(sizeof(struct d)*3); - //@ exhale Perm(xs[0].x, 1\2); + //@ exhale Perm(&xs[0].x, 1\2); free(xs); } """ @@ -112,7 +112,7 @@ class CSpec extends VercorsSpec { int main(){ struct d s1; struct d* s2 = &s1; - //@ exhale Perm(s2->x, 1\1); + //@ exhale Perm(&s2->x, 1\1); s2->x = 1; } """ @@ -124,7 +124,7 @@ class CSpec extends VercorsSpec { }; int main(){ struct d s; - //@ exhale Perm(s.x, 1\1); + //@ exhale Perm(&s.x, 1\1); s.x = 1; } """ @@ -136,7 +136,7 @@ class CSpec extends VercorsSpec { int main(){ struct d s; s.x = 1; - //@ exhale Perm(s.x, 1\1); + //@ exhale Perm(&s.x, 1\1); int x = s.x; } """ @@ -323,7 +323,7 @@ class CSpec extends VercorsSpec { int main(){ struct d s; - //@ exhale Perm(s.x, 1\1); + //@ exhale Perm(&s.x, 1\1); test(s); } """ @@ -341,7 +341,7 @@ class CSpec extends VercorsSpec { int main(){ struct d s, t; - //@ exhale Perm(s.x, 1\1); + //@ exhale Perm(&s.x, 1\1); t = s; } """ From 7b50ef3a3b3ab531990e094ac4539fde3fc75ecd Mon Sep 17 00:00:00 2001 From: Alexander Stekelenburg Date: Fri, 19 Jul 2024 10:18:02 +0200 Subject: [PATCH 08/26] Make the pointer for struct fields implicit simplifying most locations in the transformations stages --- src/rewrite/vct/rewrite/ClassToRef.scala | 36 ++- .../vct/rewrite/EncodeArrayValues.scala | 9 +- .../vct/rewrite/PrepareByValueClass.scala | 34 ++- src/rewrite/vct/rewrite/TrivialAddrOf.scala | 1 + .../vct/rewrite/VariableToPointer.scala | 23 +- .../vct/rewrite/lang/LangCPPToCol.scala | 218 +++++++----------- src/rewrite/vct/rewrite/lang/LangCToCol.scala | 43 ++-- 7 files changed, 163 insertions(+), 201 deletions(-) diff --git a/src/rewrite/vct/rewrite/ClassToRef.scala b/src/rewrite/vct/rewrite/ClassToRef.scala index aa4c140937..ec510b5ec7 100644 --- a/src/rewrite/vct/rewrite/ClassToRef.scala +++ b/src/rewrite/vct/rewrite/ClassToRef.scala @@ -7,6 +7,7 @@ import vct.col.util.AstBuildHelpers._ import hre.util.ScopedStack import vct.col.rewrite.error.{ExcludedByPassOrder, ExtraNode} import vct.col.ref.Ref +import vct.col.resolve.ctx.Referrable import vct.col.util.SuccessionMap import scala.collection.mutable @@ -34,6 +35,16 @@ case object ClassToRef extends RewriterBuilder { inner.blame(InstanceNull(inv)) } + case class DerefFieldPointerBlame( + inner: Blame[InsufficientPermission], + node: HeapDeref[_], + clazz: ByValueClass[_], + field: String, + ) extends Blame[PointerDerefError] { + override def blame(error: PointerDerefError): Unit = { + inner.blame(InsufficientPermission(node)) + } + } } case class ClassToRef[Pre <: Generation]() extends Rewriter[Pre] { @@ -323,7 +334,7 @@ case class ClassToRef[Pre <: Generation]() extends Rewriter[Pre] { val axiomType = TAxiomatic[Post](byValClassSucc.ref(cls), Nil) val (fieldFunctions, fieldInverses, fieldTypes) = cls.decls.collect { case field: Field[Pre] => - val newT = dispatch(field.t) + val newT = TNonNullPointer(dispatch(field.t)) byValFieldSucc(field) = new ADTFunction[Post]( Seq(new Variable(axiomType)(field.o)), @@ -695,17 +706,30 @@ case class ClassToRef[Pre <: Generation]() extends Rewriter[Pre] { .left(PanicBlame("incorrect instance function type?"), inv.blame), ))(inv.o) case ThisObject(_) => diz.top + case ptrOf @ AddrOf(Deref(obj, Ref(field))) + if obj.t.isInstanceOf[TByValueClass[Pre]] => + adtFunctionInvocation[Post]( + byValFieldSucc.ref(field), + args = Seq(dispatch(obj)), + )(ptrOf.o) case deref @ Deref(obj, Ref(field)) => obj.t match { case _: TByReferenceClass[Pre] => SilverDeref[Post](dispatch(obj), byRefFieldSucc.ref(field))( deref.blame )(deref.o) - case _: TByValueClass[Pre] => - adtFunctionInvocation[Post]( - byValFieldSucc.ref(field), - args = Seq(dispatch(obj)), - )(deref.o) + case t: TByValueClass[Pre] => + DerefPointer( + adtFunctionInvocation[Post]( + byValFieldSucc.ref(field), + args = Seq(dispatch(obj)), + )(deref.o) + )(DerefFieldPointerBlame( + deref.blame, + deref, + t.cls.decl.asInstanceOf[ByValueClass[Pre]], + Referrable.originNameOrEmpty(field), + ))(deref.o) } case TypeValue(t) => t match { diff --git a/src/rewrite/vct/rewrite/EncodeArrayValues.scala b/src/rewrite/vct/rewrite/EncodeArrayValues.scala index 52435b4a68..6e963afd04 100644 --- a/src/rewrite/vct/rewrite/EncodeArrayValues.scala +++ b/src/rewrite/vct/rewrite/EncodeArrayValues.scala @@ -436,10 +436,7 @@ case class EncodeArrayValues[Pre <: Generation]() extends Rewriter[Pre] { } val newFieldPerms = fields.map(member => { val loc = - (i: Variable[Post]) => - DerefPointer(Deref[Post](struct(i), member.ref)(DerefPerm))( - NonNullPointerNull - ) + (i: Variable[Post]) => Deref[Post](struct(i), member.ref)(DerefPerm) var anns: Seq[(Expr[Post], Expr[Pre] => PointerFreeError)] = Seq(( makeStruct.makePerm( i => FieldLocation[Post](struct(i), member.ref), @@ -452,7 +449,7 @@ case class EncodeArrayValues[Pre <: Generation]() extends Rewriter[Pre] { ), )) anns = - if (typeIsRef(member.t.asPointer.get.element)) + if (typeIsRef(member.t)) anns :+ ( makeStruct.makeUnique(loc), @@ -460,7 +457,7 @@ case class EncodeArrayValues[Pre <: Generation]() extends Rewriter[Pre] { ) else anns - member.t.asPointer.get.element match { + member.t match { case newStruct: TClass[Post] => // We recurse, since a field is another struct anns ++ unwrapStructPerm( diff --git a/src/rewrite/vct/rewrite/PrepareByValueClass.scala b/src/rewrite/vct/rewrite/PrepareByValueClass.scala index fdfaa81458..228e875035 100644 --- a/src/rewrite/vct/rewrite/PrepareByValueClass.scala +++ b/src/rewrite/vct/rewrite/PrepareByValueClass.scala @@ -166,19 +166,13 @@ case class PrepareByValueClass[Pre <: Generation]() extends Rewriter[Pre] { f.t match { case inner: TByValueClass[Pre] => Assign[Post]( - DerefPointer(Deref[Post](v.get, succ(f))(DerefAssignTarget))( - NonNullPointerNull - ), + Deref[Post](v.get, succ(f))(DerefAssignTarget), copyClassValue(Deref[Post](ov.get, succ(f))(blame(f)), inner, blame), )(AssignLocalOk) case _ => Assign[Post]( - DerefPointer(Deref[Post](v.get, succ(f))(DerefAssignTarget))( - NonNullPointerNull - ), - DerefPointer(Deref[Post](ov.get, succ(f))(blame(f)))( - NonNullPointerNull - ), + Deref[Post](v.get, succ(f))(DerefAssignTarget), + Deref[Post](ov.get, succ(f))(blame(f)), )(AssignLocalOk) } @@ -220,12 +214,10 @@ case class PrepareByValueClass[Pre <: Generation]() extends Rewriter[Pre] { val newFieldPerms = fields.map(member => { val loc = FieldLocation[Post](obj, succ(member)) // TODO: Don't go through regular pointers... - member.t.asPointer.get.element match { + member.t match { case inner: TByValueClass[Pre] => Perm[Post](loc, perm) &* unwrapClassPerm( - DerefPointer(Deref[Post](obj, succ(member))(blame))( - NonNullPointerNull - ), + Deref[Post](obj, succ(member))(blame), perm, inner, structType +: visited, @@ -356,12 +348,16 @@ case class PrepareByValueClass[Pre <: Generation]() extends Rewriter[Pre] { dp, v.t.asPointer.get.element.asInstanceOf[TByValueClass[Pre]], ) - case dp @ DerefPointer(Deref(_, Ref(f))) - if f.t.asPointer.get.element.isInstanceOf[TByValueClass[Pre]] => - rewriteInCopyContext( - dp, - f.t.asPointer.get.element.asInstanceOf[TByValueClass[Pre]], - ) + case deref @ Deref(_, Ref(f)) if f.t.isInstanceOf[TByValueClass[Pre]] => + if (copyContext.isEmpty) { deref.rewriteDefault() } + else { + // TODO: Improve blame message here + copyClassValue( + deref.rewriteDefault(), + f.t.asInstanceOf[TByValueClass[Pre]], + f => deref.blame, + ) + } case dp @ DerefPointer(Local(Ref(v))) if v.t.asPointer.get.element.isInstanceOf[TByValueClass[Pre]] => // This can happen if the user specifies a local of type pointer to TByValueClass diff --git a/src/rewrite/vct/rewrite/TrivialAddrOf.scala b/src/rewrite/vct/rewrite/TrivialAddrOf.scala index 63b5396c0d..6cf0e2c060 100644 --- a/src/rewrite/vct/rewrite/TrivialAddrOf.scala +++ b/src/rewrite/vct/rewrite/TrivialAddrOf.scala @@ -37,6 +37,7 @@ case class TrivialAddrOf[Pre <: Generation]() extends Rewriter[Pre] { case AddrOf(sub @ PointerSubscript(p, i)) => PointerAdd(dispatch(p), dispatch(i))(SubscriptErrorAddError(sub))(e.o) + case AddrOf(Deref(_, _)) => e.rewriteDefault() case AddrOf(other) => throw UnsupportedLocation(other) case assign @ PreAssignExpression(target, AddrOf(value)) if value.t.isInstanceOf[TByReferenceClass[Pre]] => diff --git a/src/rewrite/vct/rewrite/VariableToPointer.scala b/src/rewrite/vct/rewrite/VariableToPointer.scala index ab399c63a5..6e8546627c 100644 --- a/src/rewrite/vct/rewrite/VariableToPointer.scala +++ b/src/rewrite/vct/rewrite/VariableToPointer.scala @@ -46,8 +46,9 @@ case class VariableToPointer[Pre <: Generation]() extends Rewriter[Pre] { case AddrOf(DerefHeapVariable(Ref(v))) if !v.t.isInstanceOf[TByReferenceClass[Pre]] => v - case AddrOf(Deref(_, Ref(f))) - if !f.t.isInstanceOf[TByReferenceClass[Pre]] => + case AddrOf(Deref(o, Ref(f))) + if !f.t.isInstanceOf[TByReferenceClass[Pre]] && + !o.t.isInstanceOf[TByValueClass[Pre]] => f }) super.dispatch(program) @@ -58,15 +59,15 @@ case class VariableToPointer[Pre <: Generation]() extends Rewriter[Pre] { // TODO: Use some sort of NonNull pointer type instead case v: HeapVariable[Pre] if addressedSet.contains(v) => heapVariableMap(v) = globalDeclarations - .succeed(v, new HeapVariable(TPointer(dispatch(v.t)))(v.o)) + .succeed(v, new HeapVariable(TNonNullPointer(dispatch(v.t)))(v.o)) case v: Variable[Pre] if addressedSet.contains(v) => variableMap(v) = variables - .succeed(v, new Variable(TPointer(dispatch(v.t)))(v.o)) + .succeed(v, new Variable(TNonNullPointer(dispatch(v.t)))(v.o)) case f: InstanceField[Pre] if addressedSet.contains(f) => fieldMap(f) = classDeclarations.succeed( f, new InstanceField( - TPointer(dispatch(f.t)), + TNonNullPointer(dispatch(f.t)), f.flags.map { it => dispatch(it) }, )(f.o), ) @@ -84,7 +85,7 @@ case class VariableToPointer[Pre <: Generation]() extends Rewriter[Pre] { implicit val o: Origin = local.o Assign( Local[Post](variableMap.ref(local)), - NewPointerArray( + NewNonNullPointerArray( variableMap(local).t.asPointer.get.element, const(1), )(PanicBlame("Size is > 0")), @@ -205,13 +206,11 @@ case class VariableToPointer[Pre <: Generation]() extends Rewriter[Pre] { PointerLocation(Deref[Post](dispatch(obj), fieldMap.ref(f))(PanicBlame( "Should always be accessible" )))(PanicBlame("Should always be accessible")) - case PointerLocation( - AddrOf(Deref(obj, Ref(f))) - ) /* if addressedSet.contains(f) always true */ => + case PointerLocation(AddrOf(Deref(obj, Ref(f)))) + if addressedSet.contains(f) => FieldLocation[Post](dispatch(obj), fieldMap.ref(f)) - case PointerLocation( - AddrOf(DerefHeapVariable(Ref(v))) - ) /* if addressedSet.contains(v) always true */ => + case PointerLocation(AddrOf(DerefHeapVariable(Ref(v)))) + if addressedSet.contains(v) => HeapVariableLocation[Post](heapVariableMap.ref(v)) case PointerLocation(AddrOf(local @ Local(_))) => throw UnsupportedAddrOf(local) diff --git a/src/rewrite/vct/rewrite/lang/LangCPPToCol.scala b/src/rewrite/vct/rewrite/lang/LangCPPToCol.scala index d0708ec692..debd341db5 100644 --- a/src/rewrite/vct/rewrite/lang/LangCPPToCol.scala +++ b/src/rewrite/vct/rewrite/lang/LangCPPToCol.scala @@ -738,9 +738,7 @@ case class LangCPPToCol[Pre <: Generation](rw: LangSpecificToCol[Pre]) implicit val o: Origin = f.o Star( Perm[Post](FieldLocation[Post](thisObj, f.ref), ReadPerm()), - DerefPointer(Deref[Post](thisObj, f.ref)(new SYCLRangeDerefBlame(f)))( - NonNullPointerNull - ) >= c_const(0), + Deref[Post](thisObj, f.ref)(new SYCLRangeDerefBlame(f)) >= c_const(0), ) }) } @@ -765,9 +763,9 @@ case class LangCPPToCol[Pre <: Generation](rw: LangSpecificToCol[Pre]) implicit val o: Origin = params(i).o Star( Perm(FieldLocation[Post](result, rangeFields(i).ref), ReadPerm()), - DerefPointer(Deref[Post](result, rangeFields(i).ref)( - new SYCLRangeDerefBlame(rangeFields(i)) - ))(NonNullPointerNull) === Local[Post](params(i).ref), + Deref[Post](result, rangeFields(i).ref)(new SYCLRangeDerefBlame( + rangeFields(i) + )) === Local[Post](params(i).ref), ) }) } @@ -801,23 +799,21 @@ case class LangCPPToCol[Pre <: Generation](rw: LangSpecificToCol[Pre]) implicit val o: Origin = params(i).o foldStar(Seq( Perm(FieldLocation[Post](result, rangeFields(i).ref), ReadPerm()), - DerefPointer(Deref[Post](result, rangeFields(i).ref)( - new SYCLRangeDerefBlame(rangeFields(i)) - ))(NonNullPointerNull) === FloorDiv( + Deref[Post](result, rangeFields(i).ref)(new SYCLRangeDerefBlame( + rangeFields(i) + )) === FloorDiv( Local[Post](params(i).ref), Local[Post](params(i + 1).ref), )(ImpossibleDivByZeroBlame()), Perm(FieldLocation[Post](result, rangeFields(i + 1).ref), ReadPerm()), - DerefPointer(Deref[Post](result, rangeFields(i + 1).ref)( + Deref[Post](result, rangeFields(i + 1).ref)(new SYCLRangeDerefBlame( + rangeFields(i + 1) + )) === Local[Post](params(i + 1).ref), + Deref[Post](result, rangeFields(i).ref)(new SYCLRangeDerefBlame( + rangeFields(i) + )) * Deref[Post](result, rangeFields(i + 1).ref)( new SYCLRangeDerefBlame(rangeFields(i + 1)) - ))(NonNullPointerNull) === Local[Post](params(i + 1).ref), - DerefPointer(Deref[Post](result, rangeFields(i).ref)( - new SYCLRangeDerefBlame(rangeFields(i)) - ))(NonNullPointerNull) * DerefPointer( - Deref[Post](result, rangeFields(i + 1).ref)(new SYCLRangeDerefBlame( - rangeFields(i + 1) - )) - )(NonNullPointerNull) === Local[Post](params(i).ref), + ) === Local[Post](params(i).ref), )) }) } @@ -1098,11 +1094,9 @@ case class LangCPPToCol[Pre <: Generation](rw: LangSpecificToCol[Pre]) case _: SYCLTAccessor[Pre] => // Referencing an accessor variable can only be done in kernels, otherwise an error will already have been thrown val accessor = syclAccessorSuccessor(ref) - DerefPointer( - Deref[Post](currentThis.get, accessor.instanceField.ref)( - SYCLAccessorFieldInsufficientReferencePermissionBlame(local) - ) - )(NonNullPointerNull) + Deref[Post](currentThis.get, accessor.instanceField.ref)( + SYCLAccessorFieldInsufficientReferencePermissionBlame(local) + ) case _: SYCLTLocalAccessor[Pre] if currentKernelType.get.isInstanceOf[BasicKernel] => throw SYCLNoLocalAccessorsInBasicKernel(local) @@ -1240,18 +1234,18 @@ case class LangCPPToCol[Pre <: Generation](rw: LangSpecificToCol[Pre]) getGlobalWorkItemLinearId(inv) case "sycl::accessor::get_range" => classInstance match { - case Some(DerefPointer(Deref(_, ref))) => + case Some(Deref(_, ref)) => val accessor = syclAccessorSuccessor.values .find(acc => ref.decl.equals(acc.instanceField)).get LiteralSeq[Post]( TCInt(), accessor.rangeIndexFields.map(f => - DerefPointer(Deref[Post](currentThis.get, f.ref)( + Deref[Post](currentThis.get, f.ref)( SYCLAccessorRangeIndexFieldInsufficientReferencePermissionBlame( inv ) - ))(NonNullPointerNull) + ) ), ) case _ => throw NotApplicable(inv) @@ -1573,14 +1567,14 @@ case class LangCPPToCol[Pre <: Generation](rw: LangSpecificToCol[Pre]) val rangeFields: mutable.Buffer[InstanceField[Post]] = mutable.Buffer.empty range.indices.foreach(index => { implicit val o: Origin = range(index).o.where(name = s"range$index") - val instanceField = new InstanceField[Post](TNonNullPointer(TCInt()), Nil) + val instanceField = new InstanceField[Post](TCInt(), Nil) rangeFields.append(instanceField) val iterVar = createRangeIterVar( GlobalScope(), index, - DerefPointer(Deref[Post](currentThis.get, instanceField.ref)( - new SYCLRangeDerefBlame(instanceField) - ))(NonNullPointerNull), + Deref[Post](currentThis.get, instanceField.ref)(new SYCLRangeDerefBlame( + instanceField + )), ) currentDimensionIterVars(GlobalScope()).append(iterVar) }) @@ -1649,30 +1643,28 @@ case class LangCPPToCol[Pre <: Generation](rw: LangSpecificToCol[Pre]) { implicit val o: Origin = kernelDimensions.o .where(name = s"group_range$index") - val groupInstanceField = - new InstanceField[Post](TNonNullPointer(TCInt()), Nil) + val groupInstanceField = new InstanceField[Post](TCInt(), Nil) rangeFields.append(groupInstanceField) val groupIterVar = createRangeIterVar( GroupScope(), index, - DerefPointer(Deref[Post](currentThis.get, groupInstanceField.ref)( + Deref[Post](currentThis.get, groupInstanceField.ref)( new SYCLRangeDerefBlame(groupInstanceField) - ))(NonNullPointerNull), + ), ) currentDimensionIterVars(GroupScope()).append(groupIterVar) } { implicit val o: Origin = localRange(index).o .where(name = s"local_range$index") - val localInstanceField = - new InstanceField[Post](TNonNullPointer(TCInt()), Nil) + val localInstanceField = new InstanceField[Post](TCInt(), Nil) rangeFields.append(localInstanceField) val localIterVar = createRangeIterVar( LocalScope(), index, - DerefPointer(Deref[Post](currentThis.get, localInstanceField.ref)( + Deref[Post](currentThis.get, localInstanceField.ref)( new SYCLRangeDerefBlame(localInstanceField) - ))(NonNullPointerNull), + ), ) currentDimensionIterVars(LocalScope()).append(localIterVar) } @@ -1864,13 +1856,10 @@ case class LangCPPToCol[Pre <: Generation](rw: LangSpecificToCol[Pre]) case None => // No accessor for buffer exist in the command group, so make fields and permissions val instanceField = - new InstanceField[Post]( - TNonNullPointer(buffer.generatedVar.t), - Nil, - )(accO) + new InstanceField[Post](buffer.generatedVar.t, Nil)(accO) val rangeIndexFields = Seq .range(0, buffer.range.dimensions.size).map(i => - new InstanceField[Post](TNonNullPointer(TCInt()), Nil)( + new InstanceField[Post](TCInt(), Nil)( dimO.where(name = s"${accName}_r$i") ) ) @@ -1942,11 +1931,7 @@ case class LangCPPToCol[Pre <: Generation](rw: LangSpecificToCol[Pre]) } val rangeIndexDerefs: Seq[Expr[Post]] = acc.rangeIndexFields.map(f => - DerefPointer( - Deref[Post](classObj, f.ref)(new SYCLAccessorDimensionDerefBlame(f))( - f.o - ) - )(NonNullPointerNull)(f.o) + Deref[Post](classObj, f.ref)(new SYCLAccessorDimensionDerefBlame(f))(f.o) ) ( @@ -1964,22 +1949,18 @@ case class LangCPPToCol[Pre <: Generation](rw: LangSpecificToCol[Pre]) ReadPerm()(acc.instanceField.o), )(acc.instanceField.o), ValidArray( - DerefPointer( - Deref[Post](classObj, acc.instanceField.ref)( - new SYCLAccessorDerefBlame(acc.instanceField) - )(acc.instanceField.o) - )(NonNullPointerNull)(acc.instanceField.o), + Deref[Post](classObj, acc.instanceField.ref)( + new SYCLAccessorDerefBlame(acc.instanceField) + )(acc.instanceField.o), rangeIndexDerefs.reduce((e1, e2) => (e1 * e2)(acc.buffer.o)), )(acc.instanceField.o), ) )(acc.instanceField.o), Perm( ArrayLocation( - DerefPointer( - Deref[Post](classObj, acc.instanceField.ref)( - new SYCLAccessorDerefBlame(acc.instanceField) - )(acc.instanceField.o) - )(NonNullPointerNull)(acc.instanceField.o), + Deref[Post](classObj, acc.instanceField.ref)( + new SYCLAccessorDerefBlame(acc.instanceField) + )(acc.instanceField.o), Any()(PanicBlame( "The accessor field is not null as that was proven in the previous conditions." ))(acc.instanceField.o), @@ -2031,24 +2012,20 @@ case class LangCPPToCol[Pre <: Generation](rw: LangSpecificToCol[Pre]) constructorPostConditions.append( foldStar[Post]( Eq[Post]( - DerefPointer( - Deref[Post](result, acc.instanceField.ref)( - new SYCLAccessorDerefBlame(acc.instanceField) - )(acc.instanceField.o) - )(NonNullPointerNull)(acc.instanceField.o), + Deref[Post](result, acc.instanceField.ref)( + new SYCLAccessorDerefBlame(acc.instanceField) + )(acc.instanceField.o), Local[Post](newConstructorAccessorArg.ref)( newConstructorAccessorArg.o ), )(newConstructorAccessorArg.o) +: Seq.range(0, acc.rangeIndexFields.size).map(i => Eq[Post]( - DerefPointer( - Deref[Post](result, acc.rangeIndexFields(i).ref)( - new SYCLAccessorDimensionDerefBlame( - acc.rangeIndexFields(i) - ) - )(acc.rangeIndexFields(i).o) - )(NonNullPointerNull)(acc.rangeIndexFields(i).o), + Deref[Post](result, acc.rangeIndexFields(i).ref)( + new SYCLAccessorDimensionDerefBlame( + acc.rangeIndexFields(i) + ) + )(acc.rangeIndexFields(i).o), Local[Post](newConstructorAccessorDimensionArgs(i).ref)( newConstructorAccessorDimensionArgs(i).o ), @@ -2506,14 +2483,12 @@ case class LangCPPToCol[Pre <: Generation](rw: LangSpecificToCol[Pre]) CPP.unwrappedType(base.t) match { case SYCLTAccessor(_, 1, _) => ArraySubscript[Post]( - DerefPointer( - Deref[Post]( - currentThis.get, - syclAccessorSuccessor(base.ref.get).instanceField.ref, - )(new SYCLAccessorDerefBlame( - syclAccessorSuccessor(base.ref.get).instanceField - ))(sub.o) - )(NonNullPointerNull)(sub.o), + Deref[Post]( + currentThis.get, + syclAccessorSuccessor(base.ref.get).instanceField.ref, + )(new SYCLAccessorDerefBlame( + syclAccessorSuccessor(base.ref.get).instanceField + ))(sub.o), rw.dispatch(index), )(SYCLAccessorArraySubscriptErrorBlame(sub))(sub.o) case t: SYCLTAccessor[Pre] => @@ -2529,25 +2504,19 @@ case class LangCPPToCol[Pre <: Generation](rw: LangSpecificToCol[Pre]) val linearizeArgs = Seq( rw.dispatch(indexX), rw.dispatch(indexY), - DerefPointer( - Deref[Post](currentThis.get, accessor.rangeIndexFields(0).ref)( - new SYCLAccessorDimensionDerefBlame(accessor.rangeIndexFields(0)) - ) - )(NonNullPointerNull), - DerefPointer( - Deref[Post](currentThis.get, accessor.rangeIndexFields(1).ref)( - new SYCLAccessorDimensionDerefBlame(accessor.rangeIndexFields(1)) - ) - )(NonNullPointerNull), + Deref[Post](currentThis.get, accessor.rangeIndexFields(0).ref)( + new SYCLAccessorDimensionDerefBlame(accessor.rangeIndexFields(0)) + ), + Deref[Post](currentThis.get, accessor.rangeIndexFields(1).ref)( + new SYCLAccessorDimensionDerefBlame(accessor.rangeIndexFields(1)) + ), ) CPP.unwrappedType(base.t) match { case SYCLTAccessor(_, 2, _) => ArraySubscript[Post]( - DerefPointer( - Deref[Post](currentThis.get, accessor.instanceField.ref)( - new SYCLAccessorDerefBlame(accessor.instanceField) - ) - )(NonNullPointerNull), + Deref[Post](currentThis.get, accessor.instanceField.ref)( + new SYCLAccessorDerefBlame(accessor.instanceField) + ), syclHelperFunctions("sycl_:_:linearize_2")( linearizeArgs, SYCLAccessorArraySubscriptLinearizeInvocationBlame( @@ -2575,30 +2544,22 @@ case class LangCPPToCol[Pre <: Generation](rw: LangSpecificToCol[Pre]) rw.dispatch(indexX), rw.dispatch(indexY), rw.dispatch(indexZ), - DerefPointer( - Deref[Post](currentThis.get, accessor.rangeIndexFields(0).ref)( - new SYCLAccessorDimensionDerefBlame(accessor.rangeIndexFields(0)) - ) - )(NonNullPointerNull), - DerefPointer( - Deref[Post](currentThis.get, accessor.rangeIndexFields(1).ref)( - new SYCLAccessorDimensionDerefBlame(accessor.rangeIndexFields(1)) - ) - )(NonNullPointerNull), - DerefPointer( - Deref[Post](currentThis.get, accessor.rangeIndexFields(2).ref)( - new SYCLAccessorDimensionDerefBlame(accessor.rangeIndexFields(2)) - ) - )(NonNullPointerNull), + Deref[Post](currentThis.get, accessor.rangeIndexFields(0).ref)( + new SYCLAccessorDimensionDerefBlame(accessor.rangeIndexFields(0)) + ), + Deref[Post](currentThis.get, accessor.rangeIndexFields(1).ref)( + new SYCLAccessorDimensionDerefBlame(accessor.rangeIndexFields(1)) + ), + Deref[Post](currentThis.get, accessor.rangeIndexFields(2).ref)( + new SYCLAccessorDimensionDerefBlame(accessor.rangeIndexFields(2)) + ), ) CPP.unwrappedType(base.t) match { case SYCLTAccessor(_, 3, _) => ArraySubscript[Post]( - DerefPointer( - Deref[Post](currentThis.get, accessor.instanceField.ref)( - new SYCLAccessorDerefBlame(accessor.instanceField) - ) - )(NonNullPointerNull), + Deref[Post](currentThis.get, accessor.instanceField.ref)( + new SYCLAccessorDerefBlame(accessor.instanceField) + ), syclHelperFunctions("sycl_:_:linearize_3")( linearizeArgs, SYCLAccessorArraySubscriptLinearizeInvocationBlame( @@ -2676,12 +2637,9 @@ case class LangCPPToCol[Pre <: Generation](rw: LangSpecificToCol[Pre]) removeKernelClassInstancePermissions(right), ) - case ActionPerm(DerefPointer(Deref(obj, _)), _) - if obj.equals(this.currentThis.get) => - tt - case ModelPerm(DerefPointer(Deref(obj, _)), _) - if obj.equals(this.currentThis.get) => + case ActionPerm(Deref(obj, _), _) if obj.equals(this.currentThis.get) => tt + case ModelPerm(Deref(obj, _), _) if obj.equals(this.currentThis.get) => tt case Perm(FieldLocation(obj, _), _) if obj.equals(this.currentThis.get) => tt case PointsTo(FieldLocation(obj, _), _, _) @@ -2689,20 +2647,14 @@ case class LangCPPToCol[Pre <: Generation](rw: LangSpecificToCol[Pre]) tt case Value(FieldLocation(obj, _)) if obj.equals(this.currentThis.get) => tt - case Perm( - AmbiguousLocation(ArraySubscript(DerefPointer(Deref(obj, _)), _)), - _, - ) if obj.equals(this.currentThis.get) => + case Perm(AmbiguousLocation(ArraySubscript(Deref(obj, _), _)), _) + if obj.equals(this.currentThis.get) => tt - case PointsTo( - AmbiguousLocation(ArraySubscript(DerefPointer(Deref(obj, _)), _)), - _, - _, - ) if obj.equals(this.currentThis.get) => + case PointsTo(AmbiguousLocation(ArraySubscript(Deref(obj, _), _)), _, _) + if obj.equals(this.currentThis.get) => tt - case Value( - AmbiguousLocation(ArraySubscript(DerefPointer(Deref(obj, _)), _)) - ) if obj.equals(this.currentThis.get) => + case Value(AmbiguousLocation(ArraySubscript(Deref(obj, _), _))) + if obj.equals(this.currentThis.get) => tt case Implies(left, right) => Implies( @@ -2733,9 +2685,9 @@ case class LangCPPToCol[Pre <: Generation](rw: LangSpecificToCol[Pre]) case SYCLAccessor(buffer, SYCLReadWriteAccess(), instanceField, _) => assignLocal( buffer.generatedVar.get, - DerefPointer(Deref[Post](variable, instanceField.ref)( - new SYCLAccessorDerefBlame(instanceField) - ))(NonNullPointerNull), + Deref[Post](variable, instanceField.ref)(new SYCLAccessorDerefBlame( + instanceField + )), ) })) } diff --git a/src/rewrite/vct/rewrite/lang/LangCToCol.scala b/src/rewrite/vct/rewrite/lang/LangCToCol.scala index 54abadbe31..1b4fba2294 100644 --- a/src/rewrite/vct/rewrite/lang/LangCToCol.scala +++ b/src/rewrite/vct/rewrite/lang/LangCToCol.scala @@ -992,9 +992,10 @@ case class LangCToCol[Pre <: Generation](rw: LangSpecificToCol[Pre]) Seq(x), ) = fieldDecl fieldDecl.drop() - val t = TNonNullPointer(specs.collectFirst { - case t: CSpecificationType[Pre] => rw.dispatch(t.t) - }.get) + val t = + specs.collectFirst { case t: CSpecificationType[Pre] => + rw.dispatch(t.t) + }.get cStructFieldsSuccessor((decl, fieldDecl)) = new InstanceField(t = t, flags = Nil)(CStructFieldOrigin(x)) rw.classDeclarations @@ -1329,11 +1330,9 @@ case class LangCToCol[Pre <: Generation](rw: LangSpecificToCol[Pre]) t.t }.get if (t.isInstanceOf[CTStruct[Pre]]) { - DerefPointer[Post]( - DerefHeapVariable[Post](cGlobalNameSuccessor.ref(ref))( - local.blame - ) - )(NonNullPointerNull) + DerefHeapVariable[Post](cGlobalNameSuccessor.ref(ref))( + local.blame + ) } else { DerefHeapVariable[Post](cGlobalNameSuccessor.ref(ref))( local.blame @@ -1395,12 +1394,10 @@ case class LangCToCol[Pre <: Generation](rw: LangSpecificToCol[Pre]) case _: TNotAValue[Pre] => throw TypeUsedAsValue(deref.obj) case _ => ??? } - DerefPointer( - Deref[Post]( - rw.dispatch(deref.obj), - cStructFieldsSuccessor.ref((struct_ref.decl, struct.decls)), - )(deref.blame) - )(NonNullPointerNull) + Deref[Post]( + rw.dispatch(deref.obj), + cStructFieldsSuccessor.ref((struct_ref.decl, struct.decls)), + )(deref.blame) } } @@ -1420,12 +1417,10 @@ case class LangCToCol[Pre <: Generation](rw: LangSpecificToCol[Pre]) case CTPointer(CTStruct(struct)) => struct case t => throw WrongStructType(t) } - DerefPointer( - Deref[Post]( - DerefPointer(rw.dispatch(deref.struct))(b), - cStructFieldsSuccessor.ref((structRef.decl, struct.decls)), - )(deref.blame)(deref.o) - )(NonNullPointerNull)(deref.o) + Deref[Post]( + DerefPointer(rw.dispatch(deref.struct))(b), + cStructFieldsSuccessor.ref((structRef.decl, struct.decls)), + )(deref.blame)(deref.o) } } @@ -1449,11 +1444,9 @@ case class LangCToCol[Pre <: Generation](rw: LangSpecificToCol[Pre]) val newFieldPerms = fields.map(member => { val loc = AmbiguousLocation( - DerefPointer( - Deref[Post]( - newExpr, - cStructFieldsSuccessor.ref((structType.ref.decl, member)), - )(blame) + Deref[Post]( + newExpr, + cStructFieldsSuccessor.ref((structType.ref.decl, member)), )(blame) )(struct.blame) member.specs.collectFirst { From 1a31eddbc9eabf4fb141fd9799b71c0d3fa45dfc Mon Sep 17 00:00:00 2001 From: Alexander Stekelenburg Date: Fri, 19 Jul 2024 13:26:37 +0200 Subject: [PATCH 09/26] Fix the type numbers --- src/rewrite/vct/rewrite/ClassToRef.scala | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/rewrite/vct/rewrite/ClassToRef.scala b/src/rewrite/vct/rewrite/ClassToRef.scala index 96f640cea0..c32762cb8b 100644 --- a/src/rewrite/vct/rewrite/ClassToRef.scala +++ b/src/rewrite/vct/rewrite/ClassToRef.scala @@ -102,7 +102,7 @@ case class ClassToRef[Pre <: Generation]() extends Rewriter[Pre] { def makeTypeOf: Function[Post] = { implicit val o: Origin = TypeOfOrigin - val obj = new Variable[Post](TAnyValue()) + val obj = new Variable[Post](TRef()) withResult((result: Result[Post]) => function( blame = AbstractApplicable, @@ -185,7 +185,11 @@ case class ClassToRef[Pre <: Generation]() extends Rewriter[Pre] { "Class type parameters should be encoded using monomorphization earlier" ) - typeNumber(cls) + cls match { + case clazz: ByReferenceClass[Pre] => typeNumber(cls) + case clazz: ByValueClass[Pre] => {} + } + val thisType = dispatch(cls.classType(Nil)) cls.decls.foreach { case function: InstanceFunction[Pre] => From 3f9f02b33adcd9d37afba1f45cb7b506111b20a1 Mon Sep 17 00:00:00 2001 From: Alexander Stekelenburg Date: Fri, 19 Jul 2024 14:29:11 +0200 Subject: [PATCH 10/26] Replaced type numbers with constants for ByValueClass --- src/rewrite/vct/rewrite/ClassToRef.scala | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/rewrite/vct/rewrite/ClassToRef.scala b/src/rewrite/vct/rewrite/ClassToRef.scala index c32762cb8b..07126eadc6 100644 --- a/src/rewrite/vct/rewrite/ClassToRef.scala +++ b/src/rewrite/vct/rewrite/ClassToRef.scala @@ -185,10 +185,7 @@ case class ClassToRef[Pre <: Generation]() extends Rewriter[Pre] { "Class type parameters should be encoded using monomorphization earlier" ) - cls match { - case clazz: ByReferenceClass[Pre] => typeNumber(cls) - case clazz: ByValueClass[Pre] => {} - } + typeNumber(cls) val thisType = dispatch(cls.classType(Nil)) cls.decls.foreach { @@ -734,13 +731,18 @@ case class ClassToRef[Pre <: Generation]() extends Rewriter[Pre] { case other => ??? } case TypeOf(value) => - FunctionInvocation[Post]( - typeOf.ref(()), - Seq(dispatch(value)), - Nil, - Nil, - Nil, - )(PanicBlame("typeOf requires nothing"))(e.o) + value.t match { + case cls: TByReferenceClass[Pre] => + FunctionInvocation[Post]( + typeOf.ref(()), + Seq(dispatch(value)), + Nil, + Nil, + Nil, + )(PanicBlame("typeOf requires nothing"))(e.o) + case cls: TByValueClass[Pre] => + const[Post](typeNumber(cls.cls.decl))(e.o) + } case InstanceOf(value, TypeValue(TUnion(ts))) => implicit val o: Origin = e.o dispatch(foldOr(ts.map(t => InstanceOf(value, TypeValue(t))))) From bcd96b56f8364dac6b75b9e859f7125374c16b79 Mon Sep 17 00:00:00 2001 From: Alexander Stekelenburg Date: Fri, 19 Jul 2024 15:11:02 +0200 Subject: [PATCH 11/26] Temporarily set a fork of silicon in build.sc to test in CI --- build.sc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.sc b/build.sc index 34fd4c7f28..e0fbcd0ad3 100644 --- a/build.sc +++ b/build.sc @@ -50,8 +50,8 @@ object viper extends ScalaModule { } object siliconGit extends GitModule { - def url = T { "https://github.com/viperproject/silicon.git" } - def commitish = T { "4033dd21614b3bbba9c7615655e41c6cf0b9d80b" } + def url = T { "https://github.com/superaxander/silicon.git" } + def commitish = T { "c63989f64eb759f33bde68c330ce07d6e34134fa" } def filteredRepo = T { val workspace = repo() os.remove.all(workspace / "src" / "test") From 24197b8d42825b511083e50b6c547bfec8b54b8f Mon Sep 17 00:00:00 2001 From: Alexander Stekelenburg Date: Tue, 23 Jul 2024 09:15:41 +0200 Subject: [PATCH 12/26] Update silver, clean up unused ByValueClass axioms --- build.sc | 2 +- src/rewrite/vct/rewrite/ClassToRef.scala | 68 +------------------ .../vct/rewrite/EncodeArrayValues.scala | 1 - 3 files changed, 4 insertions(+), 67 deletions(-) diff --git a/build.sc b/build.sc index e0fbcd0ad3..00822d675d 100644 --- a/build.sc +++ b/build.sc @@ -41,7 +41,7 @@ object external extends Module { object viper extends ScalaModule { object silverGit extends GitModule { def url = T { "https://github.com/viperproject/silver.git" } - def commitish = T { "4a8065758868eae3414f86f3d96e843a283444fc" } + def commitish = T { "93bc9b7516a710c8f01438e430058c4a54e20512" } def filteredRepo = T { val workspace = repo() os.remove.all(workspace / "src" / "test") diff --git a/src/rewrite/vct/rewrite/ClassToRef.scala b/src/rewrite/vct/rewrite/ClassToRef.scala index 07126eadc6..ee01ae69a1 100644 --- a/src/rewrite/vct/rewrite/ClassToRef.scala +++ b/src/rewrite/vct/rewrite/ClassToRef.scala @@ -390,63 +390,7 @@ case class ClassToRef[Pre <: Generation]() extends Rewriter[Pre] { .getOrElse("unknown") ) ) - val destructorAxiom = - new ADTAxiom[Post](foralls( - fieldTypes, - body = - variables => { - foldAnd(variables.combinations(2).map { case Seq(v1, v2) => - v1 !== v2 - }.toSeq) ==> - foldAnd(variables.zip(fieldFunctions).map { case (v, f) => - adtFunctionInvocation[Post]( - f.ref, - args = Seq(adtFunctionInvocation[Post]( - constructor.ref, - None, - args = variables, - )), - ) === v - }) - }, - triggers = - variables => { - fieldFunctions.map { f => - Seq(adtFunctionInvocation[Post]( - f.ref, - args = Seq(adtFunctionInvocation[Post]( - constructor.ref, - None, - args = variables, - )), - )) - } - }, - )) - - // This one generates a matching loop - val injectivityAxiom1 = - new ADTAxiom[Post](foralls( - Seq(axiomType, axiomType), - body = { case Seq(a0, a1) => - foldAnd(fieldFunctions.combinations(2).map { - case Seq(f0, f1) => - Neq( - adtFunctionInvocation[Post](f0.ref, args = Seq(a0)), - adtFunctionInvocation[Post](f1.ref, args = Seq(a1)), - ) - }.toSeq) - }, - triggers = { case Seq(a0, a1) => - fieldFunctions.combinations(2).map { case Seq(f0, f1) => - Seq( - adtFunctionInvocation[Post](f0.ref, None, args = Seq(a0)), - adtFunctionInvocation[Post](f1.ref, None, args = Seq(a1)), - ) - }.toSeq - }, - )) - val injectivityAxiom2 = + val injectivityAxiom = new ADTAxiom[Post](foralls( Seq(axiomType, axiomType), body = { case Seq(a0, a1) => @@ -511,14 +455,8 @@ case class ClassToRef[Pre <: Generation]() extends Rewriter[Pre] { byValConsSucc(cls) = constructor byValClassSucc(cls) = new AxiomaticDataType[Post]( - Seq( -// constructor, -// destructorAxiom, - indexFunction, -// injectivityAxiom1, - injectivityAxiom2, - ) ++ destructorAxioms ++ indexAxioms ++ fieldFunctions ++ - fieldInverses, + Seq(indexFunction, injectivityAxiom) ++ destructorAxioms ++ + indexAxioms ++ fieldFunctions ++ fieldInverses, Nil, ) globalDeclarations.succeed(cls, byValClassSucc(cls)) diff --git a/src/rewrite/vct/rewrite/EncodeArrayValues.scala b/src/rewrite/vct/rewrite/EncodeArrayValues.scala index 6e963afd04..d7865bbda9 100644 --- a/src/rewrite/vct/rewrite/EncodeArrayValues.scala +++ b/src/rewrite/vct/rewrite/EncodeArrayValues.scala @@ -425,7 +425,6 @@ case class EncodeArrayValues[Pre <: Generation]() extends Rewriter[Pre] { ) // We do not allow this notation for recursive structs implicit val o: Origin = origin - // TODO: Instead of doing complicated stuff here just generate a Perm(struct.field, write) and rely on EncodyByValueClass to deal with it :) val fields = structType match { case t: TClass[Post] => From 653cd5fecd07b2118dd9b3b0e30ba65af9eb1d2f Mon Sep 17 00:00:00 2001 From: Alexander Stekelenburg Date: Wed, 24 Jul 2024 16:42:42 +0200 Subject: [PATCH 13/26] First working version pointer casts --- src/rewrite/vct/rewrite/ClassToRef.scala | 97 ++++-------- .../vct/rewrite/EncodeArrayValues.scala | 144 ++++++++++++++--- .../vct/rewrite/PrepareByValueClass.scala | 76 +-------- .../rewrite/SimplifyNestedQuantifiers.scala | 12 +- .../vct/rewrite/adt/ImportPointer.scala | 149 +++++++++++++++++- src/rewrite/vct/rewrite/lang/LangCToCol.scala | 12 +- .../vct/test/integration/examples/CSpec.scala | 2 +- 7 files changed, 313 insertions(+), 179 deletions(-) diff --git a/src/rewrite/vct/rewrite/ClassToRef.scala b/src/rewrite/vct/rewrite/ClassToRef.scala index ee01ae69a1..0591765789 100644 --- a/src/rewrite/vct/rewrite/ClassToRef.scala +++ b/src/rewrite/vct/rewrite/ClassToRef.scala @@ -23,9 +23,11 @@ case object ClassToRef extends RewriterBuilder { private def InstanceOfOrigin: Origin = Origin(Seq(PreferredName(Seq("subtype")), LabelContext("classToRef"))) - private val PointerCreationOrigin: Origin = Origin( - Seq(LabelContext("classToRef, pointer creation method")) - ) +// private val AsTypeOrigin: Origin = Origin( +// Seq(LabelContext("classToRef, asType function")) +// ) +// +// private val ValueAdtOrigin: Origin = Origin(Seq(PreferredName(Seq("Value")), LabelContext("classToRef"))) case class InstanceNullPreconditionFailed( inner: Blame[InstanceNull], @@ -76,26 +78,16 @@ case class ClassToRef[Pre <: Generation]() extends Rewriter[Pre] { var typeNumberStore: mutable.Map[Class[Pre], Int] = mutable.Map() val typeOf: SuccessionMap[Unit, Function[Post]] = SuccessionMap() val instanceOf: SuccessionMap[Unit, Function[Post]] = SuccessionMap() - private val pointerCreationMethods - : SuccessionMap[Type[Pre], Procedure[Post]] = SuccessionMap() - def makePointerCreationMethod(t: Type[Post]): Procedure[Post] = { - implicit val o: Origin = PointerCreationOrigin - - val result = new Variable[Post](TNonNullPointer(t)) - globalDeclarations.declare(procedure[Post]( - blame = AbstractApplicable, - contractBlame = TrueSatisfiable, - returnType = TVoid(), - outArgs = Seq(result), - ensures = UnitAccountedPredicate( - (PointerBlockLength(result.get)(FramedPtrBlockLength) === const(1)) &* - (PointerBlockOffset(result.get)(FramedPtrOffset) === const(0)) &* - Perm(PointerLocation(result.get)(FramedPtrOffset), WritePerm()) - ), - decreases = Some(DecreasesClauseNoRecursion[Post]()), - )) - } +// val valueAdt: SuccessionMap[Unit, AxiomaticDataType[Post]] = SuccessionMap() +// val valueAdtTypeArgument: SuccessionMap[Unit, Variable[Post]] = SuccessionMap() +// val asTypeFunctions: mutable.Map[Type[Pre], ADTFunction[Post]] = mutable.Map() +// +// def makeAsTypeFunction(typeName: String): ADTFunction[Post] = { +// val typeArg = valueAdtTypeArgument.getOrElseUpdate((), new Variable[Post](TType(TAnyValue()))(AsTypeOrigin.where(name="T"))) +// val value = new Variable[Post](TVar(typeArg.ref))(AsTypeOrigin.where(name="value")) +// new ADTFunction[Post](Seq(value), TNonNullPointer(TAnyValue()))(AsTypeOrigin.where(name="as_"+typeName)) +// } def typeNumber(cls: Class[Pre]): Int = typeNumberStore.getOrElseUpdate(cls, typeNumberStore.size + 1) @@ -118,7 +110,7 @@ case class ClassToRef[Pre <: Generation]() extends Rewriter[Pre] { ) } - def transitiveByValuePermissions( + private def transitiveByValuePermissions( obj: Expr[Pre], t: TByValueClass[Pre], amount: Expr[Pre], @@ -174,6 +166,10 @@ case class ClassToRef[Pre <: Generation]() extends Rewriter[Pre] { globalDeclarations.declare(typeOf(())) instanceOf(()) = makeInstanceOf globalDeclarations.declare(instanceOf(())) +// if (asTypeFunctions.nonEmpty) { +// valueAdt(()) = new AxiomaticDataType[Post](asTypeFunctions.values.toSeq, Seq(valueAdtTypeArgument(())))(ValueAdtOrigin) +// globalDeclarations.declare(valueAdt(())) +// } }._1 ) @@ -462,7 +458,7 @@ case class ClassToRef[Pre <: Generation]() extends Rewriter[Pre] { globalDeclarations.succeed(cls, byValClassSucc(cls)) case _ => cls.drop() } - case decl => rewriteDefault(decl) + case decl => super.dispatch(decl) } def instantiate(cls: Class[Pre], target: Ref[Post, Variable[Post]])( @@ -487,46 +483,7 @@ case class ClassToRef[Pre <: Generation]() extends Rewriter[Pre] { )(PanicBlame("typeOf requires nothing.")) === const(typeNumber(cls)) ), )) - case cls: ByValueClass[Pre] => throw ExtraNode -// val (assigns, vars) = -// cls.decls.collect { case field: InstanceField[Pre] => -// val element = field.t.asPointer.get.element -// val newE = dispatch(element) -// val v = new Variable[Post](TNonNullPointer(newE)) -// ( -// InvokeProcedure[Post]( -// pointerCreationMethods -// .getOrElseUpdate(element, makePointerCreationMethod(newE)) -// .ref, -// Nil, -// Seq(v.get), -// Nil, -// Nil, -// Nil, -// )(TrueSatisfiable), -// v, -// ) -// }.unzip -// val assertions = if (vars.size > 1) { -// Seq(Assert(foldAnd[Post](vars.combinations(2).map { case Seq(a,b) => a.get !== b.get}.toSeq))(PanicBlame("Newly created pointers should be distinct"))) -// } else { -// Nil -// } -// Scope( -// vars, -// Block( -// assigns ++ assertions ++ Seq( -// Assign( -// Local(target), -// adtFunctionInvocation[Post]( -// byValConsSucc.ref(cls), -// args = vars.map(_.get), -// ), -// )(AssignLocalOk) -// // TODO: Add back typeOf here (but use a separate definition for the adt) -// ) -// ), -// ) + case _: ByValueClass[Pre] => throw ExtraNode } } @@ -577,7 +534,7 @@ case class ClassToRef[Pre <: Generation]() extends Rewriter[Pre] { }, yields = yields.map { case (e, Ref(v)) => (dispatch(e), succ(v)) }, )(inv.blame)(inv.o) - case other => rewriteDefault(other) + case other => super.dispatch(other) } override def dispatch(node: ApplyAnyPredicate[Pre]): ApplyAnyPredicate[Post] = @@ -666,6 +623,8 @@ case class ClassToRef[Pre <: Generation]() extends Rewriter[Pre] { t match { case t: TClass[Pre] if t.typeArgs.isEmpty => const(typeNumber(t.cls.decl))(e.o) + // Keep pointer casts intact for the adtPointer stage + case _: TPointer[Pre] | _: TNonNullPointer[Pre] => e.rewriteDefault() case other => ??? } case TypeOf(value) => @@ -701,7 +660,7 @@ case class ClassToRef[Pre <: Generation]() extends Rewriter[Pre] { Nil, Nil, )(PanicBlame("instanceOf requires nothing"))(e.o) - case Cast(value, typeValue) => + case Cast(value, typeValue) if value.t.asClass.isDefined => dispatch( value ) // Discard for now, should assert instanceOf(value, typeValue) @@ -735,7 +694,7 @@ case class ClassToRef[Pre <: Generation]() extends Rewriter[Pre] { case v @ Value(PredicateLocation(inv: InstancePredicateApply[Pre])) => implicit val o: Origin = e.o Star[Post](v.rewrite(), dispatch(inv.obj) !== Null()) - case _ => rewriteDefault(e) + case _ => super.dispatch(e) } override def dispatch(t: Type[Pre]): Type[Post] = @@ -747,7 +706,7 @@ case class ClassToRef[Pre <: Generation]() extends Rewriter[Pre] { Nil, ) case TAnyClass() => TRef() - case t => rewriteDefault(t) + case t => super.dispatch(t) } override def dispatch(loc: Location[Pre]): Location[Post] = @@ -767,6 +726,6 @@ case class ClassToRef[Pre <: Generation]() extends Rewriter[Pre] { )(loc.o) )(NonNullPointerNull)(loc.o) } - case default => rewriteDefault(default) + case default => super.dispatch(default) } } diff --git a/src/rewrite/vct/rewrite/EncodeArrayValues.scala b/src/rewrite/vct/rewrite/EncodeArrayValues.scala index d7865bbda9..a0f967a0b2 100644 --- a/src/rewrite/vct/rewrite/EncodeArrayValues.scala +++ b/src/rewrite/vct/rewrite/EncodeArrayValues.scala @@ -117,14 +117,15 @@ case class EncodeArrayValues[Pre <: Generation]() extends Rewriter[Pre] { .Map() def makeFree( - t: Type[Post] + t: Type[Pre], + newT: Type[Post], ): (Procedure[Post], FreePointer[Pre] => PointerFreeFailed[Pre]) = { implicit val o: Origin = freeFuncOrigin var errors: Seq[Expr[Pre] => PointerFreeError] = Seq() val proc = globalDeclarations.declare({ val (vars, ptr) = variables.collect { - val a_var = new Variable[Post](TPointer(t))(o.where(name = "p")) + val a_var = new Variable[Post](TPointer(newT))(o.where(name = "p")) variables.declare(a_var) Local[Post](a_var.ref) } @@ -193,7 +194,8 @@ case class EncodeArrayValues[Pre <: Generation]() extends Rewriter[Pre] { // If structure contains structs, the permission for those fields need to be released as well val permFields = t match { - case t: TClass[Post] => unwrapStructPerm(access, t, o, makeStruct) + case t: TClass[Pre] => + unwrapStructPerm(access, None, t, o, makeStruct) case _ => Seq() } requiresT = @@ -412,33 +414,75 @@ case class EncodeArrayValues[Pre <: Generation]() extends Rewriter[Pre] { })) } + def unwrapStructCasts( + struct: Variable[Post] => Expr[Post], + pointer: Variable[Post] => Expr[Post], + structType: TClass[Pre], + origin: Origin, + makeStruct: MakeAnns, + visited: Seq[TClass[Pre]] = Seq(), + ): Seq[(Expr[Post], Expr[Pre] => PointerFreeError)] = { + if (visited.contains(structType)) { + // We do not allow this notation for recursive structs + throw UnsupportedStructPerm(origin) + } + implicit val o: Origin = origin + val field = structType.cls.decl.declarations.collectFirst { + case field: InstanceField[Pre] => field + } + if (field.isDefined) { + // TODO: I kind of want to only do this one level deep (but for every type) + val fieldType = field.get.t + val result = + if (typeIsRef(fieldType)) { + unwrapStructCasts( + (i: Variable[Post]) => + Deref[Post](struct(i), succ(field.get))(DerefPerm), + pointer, + fieldType.asInstanceOf[TClass[Pre]], + origin, + makeStruct, + structType +: visited, + ) + } else { Nil } + result :+ + (( + makeStruct.makeCast( + pointer, + (i: Variable[Post]) => + AddrOf(Deref[Post](struct(i), succ(field.get))(DerefPerm)), + TNonNullPointer(dispatch(fieldType)), + ), + // Error should never occur since this part should not be emitted in the definition of free + (p: Expr[Pre]) => GenericPointerFreeError(p), + )) + } else { Nil } + } + def unwrapStructPerm( struct: Variable[Post] => Expr[Post], - structType: TClass[Post], + // Needs to be provided to define asType assertions using casts + pointer: Option[Variable[Post] => Expr[Post]], + structType: TClass[Pre], origin: Origin, makeStruct: MakeAnns, - visited: Seq[TClass[Post]] = Seq(), + visited: Seq[TClass[Pre]] = Seq(), ): Seq[(Expr[Post], Expr[Pre] => PointerFreeError)] = { - if (visited.contains(structType)) - throw UnsupportedStructPerm( - origin - ) // We do not allow this notation for recursive structs + if (visited.contains(structType)) { + // We do not allow this notation for recursive structs + throw UnsupportedStructPerm(origin) + } implicit val o: Origin = origin - val fields = - structType match { - case t: TClass[Post] => - t.cls.decl.declarations.collect { case field: InstanceField[Post] => - field - } - case _ => Seq() - } + val fields = structType.cls.decl.declarations.collect { + case field: InstanceField[Pre] => field + } val newFieldPerms = fields.map(member => { val loc = - (i: Variable[Post]) => Deref[Post](struct(i), member.ref)(DerefPerm) + (i: Variable[Post]) => Deref[Post](struct(i), succ(member))(DerefPerm) var anns: Seq[(Expr[Post], Expr[Pre] => PointerFreeError)] = Seq(( makeStruct.makePerm( - i => FieldLocation[Post](struct(i), member.ref), + i => FieldLocation[Post](struct(i), succ(member)), IteratedPtrInjective, ), (p: Expr[Pre]) => @@ -447,6 +491,18 @@ case class EncodeArrayValues[Pre <: Generation]() extends Rewriter[Pre] { Referrable.originName(member), ), )) + if (pointer.isDefined) { + anns = + anns :+ + (( + makeStruct.makeSelfCast( + (i: Variable[Post]) => AddrOf(loc(i)), + TNonNullPointer(dispatch(member.t)), + ), + // Error should never occur since this part should not be emitted in the definition of free + (p: Expr[Pre]) => GenericPointerFreeError(p), + )) + } anns = if (typeIsRef(member.t)) anns :+ @@ -457,10 +513,11 @@ case class EncodeArrayValues[Pre <: Generation]() extends Rewriter[Pre] { else anns member.t match { - case newStruct: TClass[Post] => + case newStruct: TClass[Pre] => // We recurse, since a field is another struct anns ++ unwrapStructPerm( loc, + pointer.map { _ => (i: Variable[Post]) => AddrOf(loc(i)) }, newStruct, origin, makeStruct, @@ -470,7 +527,17 @@ case class EncodeArrayValues[Pre <: Generation]() extends Rewriter[Pre] { } }) - newFieldPerms.flatten + if (pointer.isDefined && fields.nonEmpty) { + // TODO: I kind of want to only do this one level deep (but for every type) + newFieldPerms.flatten ++ unwrapStructCasts( + struct, + pointer.get, + structType, + origin, + makeStruct, + visited, + ) + } else { newFieldPerms.flatten } } case class MakeAnns( @@ -499,6 +566,29 @@ case class EncodeArrayValues[Pre <: Generation]() extends Rewriter[Pre] { val body = (pre1 && pre2 && access(i) === access(j)) ==> (i.get === j.get) Forall(Seq(i, j), Seq(triggerUnique), body) } + + def makeCast( + pointer: Variable[Post] => Expr[Post], + fieldPointer: Variable[Post] => Expr[Post], + t: Type[Post], + ): Expr[Post] = { + implicit val o: Origin = arrayCreationOrigin + val zero = const[Post](0) + val body = (zero <= i.get && i.get < size) ==> + (Cast(pointer(i), TypeValue(t)) === Cast(fieldPointer(i), TypeValue(t))) + Forall(Seq(i), Seq(Seq(Cast(pointer(i), TypeValue(t)))), body) + } + + def makeSelfCast( + pointer: Variable[Post] => Expr[Post], + t: Type[Post], + ): Expr[Post] = { + implicit val o: Origin = arrayCreationOrigin + val zero = const[Post](0) + val body = (zero <= i.get && i.get < size) ==> + (Cast(pointer(i), TypeValue(t)) === pointer(i)) + Forall(Seq(i), Seq(Seq(Cast(pointer(i), TypeValue(t)))), body) + } } def typeIsRef(t: Type[_]): Boolean = @@ -526,6 +616,8 @@ case class EncodeArrayValues[Pre <: Generation]() extends Rewriter[Pre] { val j = new Variable[Post](TInt())(o.where(name = "j")) val access = (i: Variable[Post]) => PointerSubscript(result, i.get)(FramedPtrOffset) + val pointerAccess = + (i: Variable[Post]) => PointerAdd(result, i.get)(FramedPtrOffset) val makeStruct = MakeAnns( i, @@ -555,8 +647,9 @@ case class EncodeArrayValues[Pre <: Generation]() extends Rewriter[Pre] { else { ensures &* makeStruct.makeUnique(access) } val permFields = - dispatch(elementType) match { - case t: TClass[Post] => unwrapStructPerm(access, t, o, makeStruct) + elementType match { + case t: TClass[Pre] => + unwrapStructPerm(access, Some(pointerAccess), t, o, makeStruct) case _ => Seq() } @@ -635,11 +728,12 @@ case class EncodeArrayValues[Pre <: Generation]() extends Rewriter[Pre] { case free @ FreePointer(xs) => val newXs = dispatch(xs) val TPointer(t) = newXs.t - val (freeFunc, freeBlame) = freeMethods.getOrElseUpdate(t, makeFree(t)) + val (freeFunc, freeBlame) = freeMethods + .getOrElseUpdate(t, makeFree(xs.t.asPointer.get.element, t)) ProcedureInvocation[Post](freeFunc.ref, Seq(newXs), Nil, Nil, Nil, Nil)( freeBlame(free) )(free.o) - case other => rewriteDefault(other) + case other => super.dispatch(other) } } } diff --git a/src/rewrite/vct/rewrite/PrepareByValueClass.scala b/src/rewrite/vct/rewrite/PrepareByValueClass.scala index 228e875035..cee61769a6 100644 --- a/src/rewrite/vct/rewrite/PrepareByValueClass.scala +++ b/src/rewrite/vct/rewrite/PrepareByValueClass.scala @@ -66,19 +66,6 @@ case object PrepareByValueClass extends RewriterBuilder { extends CopyContext private case class NoCopy() extends CopyContext - - case class PointerLocationDerefBlame(blame: Blame[PointerLocationError]) - extends Blame[PointerDerefError] { - override def blame(error: PointerDerefError): Unit = { - error match { - case error: PointerLocationError => blame.blame(error) - case _ => - Unreachable( - "Blame of the respective pointer operation should be used not of DerefPointer" - ) - } - } - } } case class PrepareByValueClass[Pre <: Generation]() extends Rewriter[Pre] { @@ -213,7 +200,6 @@ case class PrepareByValueClass[Pre <: Generation]() extends Rewriter[Pre] { } val newFieldPerms = fields.map(member => { val loc = FieldLocation[Post](obj, succ(member)) - // TODO: Don't go through regular pointers... member.t match { case inner: TByValueClass[Pre] => Perm[Post](loc, perm) &* unwrapClassPerm( @@ -229,38 +215,6 @@ case class PrepareByValueClass[Pre <: Generation]() extends Rewriter[Pre] { foldStar(newFieldPerms) } - private def unwrapClassComp( - comp: (Expr[Post], Expr[Post]) => Expr[Post], - left: Expr[Post], - right: Expr[Post], - structType: TByValueClass[Pre], - visited: Seq[TByValueClass[Pre]] = Nil, - )(implicit o: Origin): Expr[Post] = { - // TODO: Better error - if (visited.contains(structType)) - throw UnsupportedStructPerm(o) - - val blame = PanicBlame("Struct deref can never fail") - val fields = structType.cls.decl.decls.collect { - case f: InstanceField[Pre] => f - } - foldAnd(fields.map(member => { - val l = - RawDerefPointer(Deref[Post](left, succ(member))(blame))( - NonNullPointerNull - ) - val r = - RawDerefPointer(Deref[Post](right, succ(member))(blame))( - NonNullPointerNull - ) - member.t match { -// case p: TNonNullPointer[Pre] if p.element.isInstanceOf[TByValueClass[Pre]] => -// unwrapClassComp(comp, DerefPointer(l)(NonNullPointerNull), r, p.element.asInstanceOf[TByValueClass[Pre]], structType +: visited) - case _ => comp(l, r) - } - })) - } - override def dispatch(node: Expr[Pre]): Expr[Post] = { implicit val o: Origin = node.o node match { @@ -272,27 +226,7 @@ case class PrepareByValueClass[Pre <: Generation]() extends Rewriter[Pre] { .ref, ) } -// case Eq(left, right) -// if left.t == right.t && left.t.isInstanceOf[TByValueClass[Pre]] => -// val newLeft = dispatch(left) -// val newRight = dispatch(right) -// return Eq(newLeft, newRight) && unwrapClassComp( -// (l, r) => Eq(l, r), -// newLeft, -// newRight, -// left.t.asInstanceOf[TByValueClass[Pre]], -// ) -// case Neq(left, right) -// if left.t == right.t && left.t.isInstanceOf[TByValueClass[Pre]] => -// val newLeft = dispatch(left) -// val newRight = dispatch(right) -// return Neq(newLeft, newRight) && unwrapClassComp( -// (l, r) => Neq(l, r), -// newLeft, -// newRight, -// left.t.asInstanceOf[TByValueClass[Pre]], -// ) - case _ => {} + case _ => } if (inAssignment.nonEmpty) node.rewriteDefault() @@ -315,9 +249,6 @@ case class PrepareByValueClass[Pre <: Generation]() extends Rewriter[Pre] { newP, ) } else { node.rewriteDefault() } - // What if I get rid of this... -// case Perm(loc@PointerLocation(e), p) if e.t.asPointer.exists(t => t.element.isInstanceOf[TByValueClass[Pre]])=> -// unwrapClassPerm(DerefPointer(dispatch(e))(PointerLocationDerefBlame(loc.blame))(loc.o), dispatch(p), e.t.asPointer.get.element.asInstanceOf[TByValueClass[Pre]]) case assign: PreAssignExpression[Pre] => val target = inAssignment.having(()) { dispatch(assign.target) } if (assign.target.t.isInstanceOf[TByValueClass[Pre]]) { @@ -328,20 +259,19 @@ case class PrepareByValueClass[Pre <: Generation]() extends Rewriter[Pre] { // No need for copy semantics in this context copyContext.having(NoCopy()) { assign.rewrite(target = target) } } - case invocation: Invocation[Pre] => { + case invocation: Invocation[Pre] => invocation.rewrite(args = invocation.args.map { a => if (a.t.isInstanceOf[TByValueClass[Pre]]) { copyContext.having(InCall(invocation)) { dispatch(a) } } else { copyContext.having(NoCopy()) { dispatch(a) } } }) - } - // WHOOPSIE WE ALSO MAKE A COPY IF IT WAS A POINTER case dp @ DerefPointer(HeapLocal(Ref(v))) if v.t.asPointer.get.element.isInstanceOf[TByValueClass[Pre]] => rewriteInCopyContext( dp, v.t.asPointer.get.element.asInstanceOf[TByValueClass[Pre]], ) + // TODO: Check for copy semantics in inappropriate places (i.e. when the user has made this a pointer) case dp @ DerefPointer(DerefHeapVariable(Ref(v))) if v.t.asPointer.get.element.isInstanceOf[TByValueClass[Pre]] => rewriteInCopyContext( diff --git a/src/rewrite/vct/rewrite/SimplifyNestedQuantifiers.scala b/src/rewrite/vct/rewrite/SimplifyNestedQuantifiers.scala index 968cd7d6e4..9ee0d0da11 100644 --- a/src/rewrite/vct/rewrite/SimplifyNestedQuantifiers.scala +++ b/src/rewrite/vct/rewrite/SimplifyNestedQuantifiers.scala @@ -78,11 +78,13 @@ case class SimplifyNestedQuantifiers[Pre <: Generation]() case e: Forall[Pre] => topLevel = false equalityChecker = ExpressionEqualityCheck(Some(infoGetter.finalInfo())) - mapUnfoldedStar( - e.body, - (b: Expr[Pre]) => - rewriteBinder(Forall(e.bindings, e.triggers, b)(e.o)), - ) + if (e.bindings.size > 1) { + mapUnfoldedStar( + e.body, + (b: Expr[Pre]) => + rewriteBinder(Forall(e.bindings, e.triggers, b)(e.o)), + ) + } else { e.rewriteDefault() } case e: Starall[Pre] => topLevel = false equalityChecker = ExpressionEqualityCheck(Some(infoGetter.finalInfo())) diff --git a/src/rewrite/vct/rewrite/adt/ImportPointer.scala b/src/rewrite/vct/rewrite/adt/ImportPointer.scala index 9c5d253e85..c67fb2a6c4 100644 --- a/src/rewrite/vct/rewrite/adt/ImportPointer.scala +++ b/src/rewrite/vct/rewrite/adt/ImportPointer.scala @@ -5,7 +5,7 @@ import ImportADT.typeText import vct.col.origin._ import vct.col.ref.Ref import vct.col.rewrite.Generation -import vct.col.util.AstBuildHelpers._ +import vct.col.util.AstBuildHelpers.{functionInvocation, _} import vct.col.util.SuccessionMap import scala.collection.mutable @@ -18,6 +18,10 @@ case object ImportPointer extends ImportADTBuilder("pointer") { Seq(LabelContext("adtPointer, pointer creation method")) ) + private val AsTypeOrigin: Origin = Origin( + Seq(LabelContext("classToRef, asType function")) + ) + case class PointerNullOptNone(inner: Blame[PointerNull], expr: Expr[_]) extends Blame[OptionNone] { override def blame(error: OptionNone): Unit = inner.blame(PointerNull(expr)) @@ -85,6 +89,23 @@ case class ImportPointer[Pre <: Generation](importer: ImportADTImporter) private val pointerCreationMethods : SuccessionMap[Type[Pre], Procedure[Post]] = SuccessionMap() + val asTypeFunctions: mutable.Map[Type[Pre], Function[Post]] = mutable.Map() + + private def makeAsTypeFunction(typeName: String): Function[Post] = { + val value = + new Variable[Post](TAxiomatic(pointerAdt.ref, Nil))( + AsTypeOrigin.where(name = "value") + ) + globalDeclarations.declare( + function[Post]( + AbstractApplicable, + TrueSatisfiable, + returnType = TAxiomatic(pointerAdt.ref, Nil), + args = Seq(value), + )(AsTypeOrigin.where(name = "as_" + typeName)) + ) + } + private def makePointerCreationMethod(t: Type[Post]): Procedure[Post] = { implicit val o: Origin = PointerCreationOrigin .where(name = "create_nonnull_pointer_" + t.toString) @@ -169,7 +190,7 @@ case class ImportPointer[Pre <: Generation](importer: ImportADTImporter) t match { case TPointer(_) => TOption(TAxiomatic(pointerAdt.ref, Nil)) case TNonNullPointer(_) => TAxiomatic(pointerAdt.ref, Nil) - case other => rewriteDefault(other) + case other => super.postCoerce(other) } override def postCoerce(location: Location[Pre]): Location[Post] = { @@ -380,7 +401,129 @@ case class ImportPointer[Pre <: Generation](importer: ImportADTImporter) PointerBlockLength(pointer)(pointerLen.blame) - PointerBlockOffset(pointer)(pointerLen.blame) ) - case other => rewriteDefault(other) + case Cast(value, typeValue) if value.t.asPointer.isDefined => + // TODO: Check if types are compatible + // TODO: Clean up code duplication + val targetType = typeValue.t.asInstanceOf[TType[Pre]].t + val innerType = targetType.asPointer.get.element + val newValue = dispatch(value) + (targetType, value.t) match { + case (TPointer(_), TPointer(_)) => + Select[Post]( + newValue === OptNone(), + OptNoneTyped(TAxiomatic(pointerAdt.ref, Nil)), + OptSome(functionInvocation[Post]( + PanicBlame("as_type requires nothing"), + asTypeFunctions.getOrElseUpdate( + innerType, + makeAsTypeFunction(innerType.toString), + ).ref, + Seq(value match { + case PointerAdd(_, _) => + OptGet(newValue)(PanicBlame( + "OptGet(Some(_)) should always be optimised away" + )) + case _ => + FunctionInvocation[Post]( + ref = pointerAdd.ref, + // Always index with zero, otherwise quantifiers with pointers do not get triggered + args = Seq( + OptGet(newValue)(PanicBlame( + "Can never be null since this is ensured in the conditional statement" + )), + const(0), + ), + typeArgs = Nil, + Nil, + Nil, + )(PanicBlame( + "Pointer out of bounds in pointer cast (no appropriate blame available)" + )) + }), + )), + ) + case (TNonNullPointer(_), TPointer(_)) => + functionInvocation[Post]( + PanicBlame("as_type requires nothing"), + asTypeFunctions.getOrElseUpdate( + innerType, + makeAsTypeFunction(innerType.toString), + ).ref, + Seq(value match { + case PointerAdd(_, _) => + OptGet(newValue)(PanicBlame( + "OptGet(Some(_)) should always be optimised away" + )) + case _ => + FunctionInvocation[Post]( + ref = pointerAdd.ref, + // Always index with zero, otherwise quantifiers with pointers do not get triggered + args = Seq( + OptGet(newValue)(PanicBlame( + "Casting a pointer to a non-null pointer implies the pointer must be statically known to be non-null" + )), + const(0), + ), + typeArgs = Nil, + Nil, + Nil, + )(PanicBlame( + "Pointer out of bounds in pointer cast (no appropriate blame available)" + )) + }), + ) + case (TPointer(_), TNonNullPointer(_)) => + OptSome(functionInvocation[Post]( + PanicBlame("as_type requires nothing"), + asTypeFunctions.getOrElseUpdate( + innerType, + makeAsTypeFunction(innerType.toString), + ).ref, + Seq(value match { + case PointerAdd(_, _) => + OptGet(newValue)(PanicBlame( + "OptGet(Some(_)) should always be optimised away" + )) + case _ => + FunctionInvocation[Post]( + ref = pointerAdd.ref, + // Always index with zero, otherwise quantifiers with pointers do not get triggered + args = Seq(newValue, const(0)), + typeArgs = Nil, + Nil, + Nil, + )(PanicBlame( + "Pointer out of bounds in pointer cast (no appropriate blame available)" + )) + }), + )) + case (TNonNullPointer(_), TNonNullPointer(_)) => + functionInvocation[Post]( + PanicBlame("as_type requires nothing"), + asTypeFunctions.getOrElseUpdate( + innerType, + makeAsTypeFunction(innerType.toString), + ).ref, + Seq(value match { + case PointerAdd(_, _) => + OptGet(newValue)(PanicBlame( + "OptGet(Some(_)) should always be optimised away" + )) + case _ => + FunctionInvocation[Post]( + ref = pointerAdd.ref, + // Always index with zero, otherwise quantifiers with pointers do not get triggered + args = Seq(newValue, const(0)), + typeArgs = Nil, + Nil, + Nil, + )(PanicBlame( + "Pointer out of bounds in pointer cast (no appropriate blame available)" + )) + }), + ) + } + case other => super.postCoerce(other) } } } diff --git a/src/rewrite/vct/rewrite/lang/LangCToCol.scala b/src/rewrite/vct/rewrite/lang/LangCToCol.scala index a5e11cdf44..517442ae0a 100644 --- a/src/rewrite/vct/rewrite/lang/LangCToCol.scala +++ b/src/rewrite/vct/rewrite/lang/LangCToCol.scala @@ -412,6 +412,9 @@ case class LangCToCol[Pre <: Generation](rw: LangSpecificToCol[Pre]) case CCast(CInvocation(CLocal("__vercors_malloc"), _, _, _), _) => throw UnsupportedMalloc(c) case CCast(n @ Null(), t) if t.asPointer.isDefined => rw.dispatch(n) + // TODO: Check if valid pointer cast + case CCast(e, t) if e.t.asPointer.isDefined && t.asPointer.isDefined => + Cast(rw.dispatch(e), TypeValue(rw.dispatch(t))(t.o))(c.o) case _ => throw UnsupportedCast(c) } @@ -1095,6 +1098,7 @@ case class LangCToCol[Pre <: Generation](rw: LangSpecificToCol[Pre]) } } + // TODO: (AS) Fixed-size arrays seem to become pointers but they're actually value types def rewriteArrayDeclaration( decl: CLocalDeclaration[Pre], cta: CTArray[Pre], @@ -1330,9 +1334,11 @@ case class LangCToCol[Pre <: Generation](rw: LangSpecificToCol[Pre]) t.t }.get if (t.isInstanceOf[CTStruct[Pre]]) { - DerefHeapVariable[Post](cGlobalNameSuccessor.ref(ref))( - local.blame - ) + DerefPointer( + DerefHeapVariable[Post](cGlobalNameSuccessor.ref(ref))( + local.blame + ) + )(local.blame) } else { DerefHeapVariable[Post](cGlobalNameSuccessor.ref(ref))( local.blame diff --git a/test/main/vct/test/integration/examples/CSpec.scala b/test/main/vct/test/integration/examples/CSpec.scala index d8209460ad..9526f29bbe 100644 --- a/test/main/vct/test/integration/examples/CSpec.scala +++ b/test/main/vct/test/integration/examples/CSpec.scala @@ -141,7 +141,7 @@ class CSpec extends VercorsSpec { } """ - vercors should error withCode "unsupportedCast" in "Cast ptr struct to int" c + vercors should verify using silicon in "Cast ptr struct to int" c """ struct d{ int x; From 88305b8c54cc27ef8c38aeab0edea47166f4c1aa Mon Sep 17 00:00:00 2001 From: Alexander Stekelenburg Date: Wed, 24 Jul 2024 17:20:19 +0200 Subject: [PATCH 14/26] Also get rid of casts from Object to another class --- src/rewrite/vct/rewrite/ClassToRef.scala | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/rewrite/vct/rewrite/ClassToRef.scala b/src/rewrite/vct/rewrite/ClassToRef.scala index 0591765789..ca771b1f6c 100644 --- a/src/rewrite/vct/rewrite/ClassToRef.scala +++ b/src/rewrite/vct/rewrite/ClassToRef.scala @@ -660,7 +660,9 @@ case class ClassToRef[Pre <: Generation]() extends Rewriter[Pre] { Nil, Nil, )(PanicBlame("instanceOf requires nothing"))(e.o) - case Cast(value, typeValue) if value.t.asClass.isDefined => + case Cast(value, typeValue) + if value.t.asClass.isDefined || + value.t.isInstanceOf[TAnyClass[Pre]] => dispatch( value ) // Discard for now, should assert instanceOf(value, typeValue) From d736fdd09f9db77e65cbcdcb379c91ea2af56f9e Mon Sep 17 00:00:00 2001 From: Alexander Stekelenburg Date: Thu, 25 Jul 2024 09:53:03 +0200 Subject: [PATCH 15/26] Ignore quantifier in SimplifyNestedQuantifiers if it has a trigger and clean up ClassToRef --- src/rewrite/vct/rewrite/ClassToRef.scala | 20 ------------- .../rewrite/SimplifyNestedQuantifiers.scala | 30 +++++++++++-------- 2 files changed, 18 insertions(+), 32 deletions(-) diff --git a/src/rewrite/vct/rewrite/ClassToRef.scala b/src/rewrite/vct/rewrite/ClassToRef.scala index ca771b1f6c..5ca41f2cd4 100644 --- a/src/rewrite/vct/rewrite/ClassToRef.scala +++ b/src/rewrite/vct/rewrite/ClassToRef.scala @@ -23,12 +23,6 @@ case object ClassToRef extends RewriterBuilder { private def InstanceOfOrigin: Origin = Origin(Seq(PreferredName(Seq("subtype")), LabelContext("classToRef"))) -// private val AsTypeOrigin: Origin = Origin( -// Seq(LabelContext("classToRef, asType function")) -// ) -// -// private val ValueAdtOrigin: Origin = Origin(Seq(PreferredName(Seq("Value")), LabelContext("classToRef"))) - case class InstanceNullPreconditionFailed( inner: Blame[InstanceNull], inv: InvokingNode[_], @@ -79,16 +73,6 @@ case class ClassToRef[Pre <: Generation]() extends Rewriter[Pre] { val typeOf: SuccessionMap[Unit, Function[Post]] = SuccessionMap() val instanceOf: SuccessionMap[Unit, Function[Post]] = SuccessionMap() -// val valueAdt: SuccessionMap[Unit, AxiomaticDataType[Post]] = SuccessionMap() -// val valueAdtTypeArgument: SuccessionMap[Unit, Variable[Post]] = SuccessionMap() -// val asTypeFunctions: mutable.Map[Type[Pre], ADTFunction[Post]] = mutable.Map() -// -// def makeAsTypeFunction(typeName: String): ADTFunction[Post] = { -// val typeArg = valueAdtTypeArgument.getOrElseUpdate((), new Variable[Post](TType(TAnyValue()))(AsTypeOrigin.where(name="T"))) -// val value = new Variable[Post](TVar(typeArg.ref))(AsTypeOrigin.where(name="value")) -// new ADTFunction[Post](Seq(value), TNonNullPointer(TAnyValue()))(AsTypeOrigin.where(name="as_"+typeName)) -// } - def typeNumber(cls: Class[Pre]): Int = typeNumberStore.getOrElseUpdate(cls, typeNumberStore.size + 1) @@ -166,10 +150,6 @@ case class ClassToRef[Pre <: Generation]() extends Rewriter[Pre] { globalDeclarations.declare(typeOf(())) instanceOf(()) = makeInstanceOf globalDeclarations.declare(instanceOf(())) -// if (asTypeFunctions.nonEmpty) { -// valueAdt(()) = new AxiomaticDataType[Post](asTypeFunctions.values.toSeq, Seq(valueAdtTypeArgument(())))(ValueAdtOrigin) -// globalDeclarations.declare(valueAdt(())) -// } }._1 ) diff --git a/src/rewrite/vct/rewrite/SimplifyNestedQuantifiers.scala b/src/rewrite/vct/rewrite/SimplifyNestedQuantifiers.scala index 9ee0d0da11..8d51e2b0e1 100644 --- a/src/rewrite/vct/rewrite/SimplifyNestedQuantifiers.scala +++ b/src/rewrite/vct/rewrite/SimplifyNestedQuantifiers.scala @@ -78,13 +78,11 @@ case class SimplifyNestedQuantifiers[Pre <: Generation]() case e: Forall[Pre] => topLevel = false equalityChecker = ExpressionEqualityCheck(Some(infoGetter.finalInfo())) - if (e.bindings.size > 1) { - mapUnfoldedStar( - e.body, - (b: Expr[Pre]) => - rewriteBinder(Forall(e.bindings, e.triggers, b)(e.o)), - ) - } else { e.rewriteDefault() } + mapUnfoldedStar( + e.body, + (b: Expr[Pre]) => + rewriteBinder(Forall(e.bindings, e.triggers, b)(e.o)), + ) case e: Starall[Pre] => topLevel = false equalityChecker = ExpressionEqualityCheck(Some(infoGetter.finalInfo())) @@ -227,6 +225,18 @@ case class SimplifyNestedQuantifiers[Pre <: Generation]() )(contract.blame)(contract.o) } + private def hasTriggers(e: Binder[Pre]): Boolean = + e match { + case Forall(body, triggers, _) => + triggers.exists(_.nonEmpty) || body.exists { + case InlinePattern(_, _, _) | InLinePatternLocation(_, _) => true + } + case Starall(body, triggers, _) => + triggers.exists(_.nonEmpty) || body.exists { + case InlinePattern(_, _, _) | InLinePatternLocation(_, _) => true + } + } + def rewriteLinearArray(e: Binder[Pre]): Option[Expr[Post]] = { val originalBody = e match { @@ -239,11 +249,7 @@ case class SimplifyNestedQuantifiers[Pre <: Generation]() return None // PB: do not attempt to reshape quantifiers that already have patterns - if ( - originalBody.exists { - case InlinePattern(_, _, _) | InLinePatternLocation(_, _) => true - } - ) { + if (hasTriggers(e)) { logger.debug(s"Not rewriting $e because it contains patterns") return None } From 261536115eacfaee11a19b9bfeacbd40564ff738 Mon Sep 17 00:00:00 2001 From: Alexander Stekelenburg Date: Thu, 25 Jul 2024 10:33:21 +0200 Subject: [PATCH 16/26] Fix compilation error --- src/rewrite/vct/rewrite/SimplifyNestedQuantifiers.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rewrite/vct/rewrite/SimplifyNestedQuantifiers.scala b/src/rewrite/vct/rewrite/SimplifyNestedQuantifiers.scala index 8d51e2b0e1..b85541768e 100644 --- a/src/rewrite/vct/rewrite/SimplifyNestedQuantifiers.scala +++ b/src/rewrite/vct/rewrite/SimplifyNestedQuantifiers.scala @@ -227,11 +227,11 @@ case class SimplifyNestedQuantifiers[Pre <: Generation]() private def hasTriggers(e: Binder[Pre]): Boolean = e match { - case Forall(body, triggers, _) => + case Forall(_, triggers, body) => triggers.exists(_.nonEmpty) || body.exists { case InlinePattern(_, _, _) | InLinePatternLocation(_, _) => true } - case Starall(body, triggers, _) => + case Starall(_, triggers, body) => triggers.exists(_.nonEmpty) || body.exists { case InlinePattern(_, _, _) | InLinePatternLocation(_, _) => true } From 6c8be0afc8e2747f4c54f19844f60201bca62c15 Mon Sep 17 00:00:00 2001 From: Alexander Stekelenburg Date: Thu, 25 Jul 2024 11:05:40 +0200 Subject: [PATCH 17/26] Reduce code duplication in adtPointer, remove all non-pointer casts in classToRef --- src/rewrite/vct/rewrite/ClassToRef.scala | 4 +- .../vct/rewrite/adt/ImportPointer.scala | 148 +++++------------- 2 files changed, 44 insertions(+), 108 deletions(-) diff --git a/src/rewrite/vct/rewrite/ClassToRef.scala b/src/rewrite/vct/rewrite/ClassToRef.scala index 5ca41f2cd4..a606a9a908 100644 --- a/src/rewrite/vct/rewrite/ClassToRef.scala +++ b/src/rewrite/vct/rewrite/ClassToRef.scala @@ -640,9 +640,7 @@ case class ClassToRef[Pre <: Generation]() extends Rewriter[Pre] { Nil, Nil, )(PanicBlame("instanceOf requires nothing"))(e.o) - case Cast(value, typeValue) - if value.t.asClass.isDefined || - value.t.isInstanceOf[TAnyClass[Pre]] => + case Cast(value, typeValue) if value.t.asPointer.isEmpty => dispatch( value ) // Discard for now, should assert instanceOf(value, typeValue) diff --git a/src/rewrite/vct/rewrite/adt/ImportPointer.scala b/src/rewrite/vct/rewrite/adt/ImportPointer.scala index c67fb2a6c4..8419c465a3 100644 --- a/src/rewrite/vct/rewrite/adt/ImportPointer.scala +++ b/src/rewrite/vct/rewrite/adt/ImportPointer.scala @@ -403,7 +403,6 @@ case class ImportPointer[Pre <: Generation](importer: ImportADTImporter) ) case Cast(value, typeValue) if value.t.asPointer.isDefined => // TODO: Check if types are compatible - // TODO: Clean up code duplication val targetType = typeValue.t.asInstanceOf[TType[Pre]].t val innerType = targetType.asPointer.get.element val newValue = dispatch(value) @@ -412,118 +411,57 @@ case class ImportPointer[Pre <: Generation](importer: ImportADTImporter) Select[Post]( newValue === OptNone(), OptNoneTyped(TAxiomatic(pointerAdt.ref, Nil)), - OptSome(functionInvocation[Post]( - PanicBlame("as_type requires nothing"), - asTypeFunctions.getOrElseUpdate( - innerType, - makeAsTypeFunction(innerType.toString), - ).ref, - Seq(value match { - case PointerAdd(_, _) => - OptGet(newValue)(PanicBlame( - "OptGet(Some(_)) should always be optimised away" - )) - case _ => - FunctionInvocation[Post]( - ref = pointerAdd.ref, - // Always index with zero, otherwise quantifiers with pointers do not get triggered - args = Seq( - OptGet(newValue)(PanicBlame( - "Can never be null since this is ensured in the conditional statement" - )), - const(0), - ), - typeArgs = Nil, - Nil, - Nil, - )(PanicBlame( - "Pointer out of bounds in pointer cast (no appropriate blame available)" - )) - }), + OptSome(applyAsTypeFunction( + innerType, + value, + OptGet(newValue)(PanicBlame( + "Can never be null since this is ensured in the conditional expression" + )), )), ) case (TNonNullPointer(_), TPointer(_)) => - functionInvocation[Post]( - PanicBlame("as_type requires nothing"), - asTypeFunctions.getOrElseUpdate( - innerType, - makeAsTypeFunction(innerType.toString), - ).ref, - Seq(value match { - case PointerAdd(_, _) => - OptGet(newValue)(PanicBlame( - "OptGet(Some(_)) should always be optimised away" - )) - case _ => - FunctionInvocation[Post]( - ref = pointerAdd.ref, - // Always index with zero, otherwise quantifiers with pointers do not get triggered - args = Seq( - OptGet(newValue)(PanicBlame( - "Casting a pointer to a non-null pointer implies the pointer must be statically known to be non-null" - )), - const(0), - ), - typeArgs = Nil, - Nil, - Nil, - )(PanicBlame( - "Pointer out of bounds in pointer cast (no appropriate blame available)" - )) - }), + applyAsTypeFunction( + innerType, + value, + OptGet(newValue)(PanicBlame( + "Casting a pointer to a non-null pointer implies the pointer must be statically known to be non-null" + )), ) case (TPointer(_), TNonNullPointer(_)) => - OptSome(functionInvocation[Post]( - PanicBlame("as_type requires nothing"), - asTypeFunctions.getOrElseUpdate( - innerType, - makeAsTypeFunction(innerType.toString), - ).ref, - Seq(value match { - case PointerAdd(_, _) => - OptGet(newValue)(PanicBlame( - "OptGet(Some(_)) should always be optimised away" - )) - case _ => - FunctionInvocation[Post]( - ref = pointerAdd.ref, - // Always index with zero, otherwise quantifiers with pointers do not get triggered - args = Seq(newValue, const(0)), - typeArgs = Nil, - Nil, - Nil, - )(PanicBlame( - "Pointer out of bounds in pointer cast (no appropriate blame available)" - )) - }), - )) + OptSome(applyAsTypeFunction(innerType, value, newValue)) case (TNonNullPointer(_), TNonNullPointer(_)) => - functionInvocation[Post]( - PanicBlame("as_type requires nothing"), - asTypeFunctions.getOrElseUpdate( - innerType, - makeAsTypeFunction(innerType.toString), - ).ref, - Seq(value match { - case PointerAdd(_, _) => - OptGet(newValue)(PanicBlame( - "OptGet(Some(_)) should always be optimised away" - )) - case _ => - FunctionInvocation[Post]( - ref = pointerAdd.ref, - // Always index with zero, otherwise quantifiers with pointers do not get triggered - args = Seq(newValue, const(0)), - typeArgs = Nil, - Nil, - Nil, - )(PanicBlame( - "Pointer out of bounds in pointer cast (no appropriate blame available)" - )) - }), - ) + applyAsTypeFunction(innerType, value, newValue) } case other => super.postCoerce(other) } } + + private def applyAsTypeFunction( + innerType: Type[Pre], + preExpr: Expr[Pre], + postExpr: Expr[Post], + )(implicit o: Origin): Expr[Post] = { + functionInvocation[Post]( + PanicBlame("as_type requires nothing"), + asTypeFunctions + .getOrElseUpdate(innerType, makeAsTypeFunction(innerType.toString)).ref, + Seq(preExpr match { + case PointerAdd(_, _) => + OptGet(postExpr)(PanicBlame( + "OptGet(Some(_)) should always be optimised away" + )) + case _ => + FunctionInvocation[Post]( + ref = pointerAdd.ref, + // Always index with zero, otherwise quantifiers with pointers do not get triggered + args = Seq(postExpr, const(0)), + typeArgs = Nil, + Nil, + Nil, + )(PanicBlame( + "Pointer out of bounds in pointer cast (no appropriate blame available)" + )) + }), + ) + } } From e141bcf6d0279c284d5a180f44ff0526ae47899e Mon Sep 17 00:00:00 2001 From: Alexander Stekelenburg Date: Thu, 25 Jul 2024 13:43:40 +0200 Subject: [PATCH 18/26] Fix duplicate OptGet and add asType function to primitive pointer arrays --- src/rewrite/vct/rewrite/EncodeArrayValues.scala | 8 +++++++- src/rewrite/vct/rewrite/adt/ImportPointer.scala | 5 +---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/rewrite/vct/rewrite/EncodeArrayValues.scala b/src/rewrite/vct/rewrite/EncodeArrayValues.scala index a0f967a0b2..9f135261c0 100644 --- a/src/rewrite/vct/rewrite/EncodeArrayValues.scala +++ b/src/rewrite/vct/rewrite/EncodeArrayValues.scala @@ -650,7 +650,13 @@ case class EncodeArrayValues[Pre <: Generation]() extends Rewriter[Pre] { elementType match { case t: TClass[Pre] => unwrapStructPerm(access, Some(pointerAccess), t, o, makeStruct) - case _ => Seq() + case t => + Seq(( + makeStruct + .makeSelfCast(pointerAccess, TNonNullPointer(dispatch(t))), + // Will never be used + (p: Expr[Pre]) => GenericPointerFreeError(p), + )) } ensures = diff --git a/src/rewrite/vct/rewrite/adt/ImportPointer.scala b/src/rewrite/vct/rewrite/adt/ImportPointer.scala index 8419c465a3..89c7c4e6c5 100644 --- a/src/rewrite/vct/rewrite/adt/ImportPointer.scala +++ b/src/rewrite/vct/rewrite/adt/ImportPointer.scala @@ -446,10 +446,7 @@ case class ImportPointer[Pre <: Generation](importer: ImportADTImporter) asTypeFunctions .getOrElseUpdate(innerType, makeAsTypeFunction(innerType.toString)).ref, Seq(preExpr match { - case PointerAdd(_, _) => - OptGet(postExpr)(PanicBlame( - "OptGet(Some(_)) should always be optimised away" - )) + case PointerAdd(_, _) => postExpr case _ => FunctionInvocation[Post]( ref = pointerAdd.ref, From 401c3d9fc9190f89e5575dd5c5b1cd7ddb92b77d Mon Sep 17 00:00:00 2001 From: Alexander Stekelenburg Date: Thu, 25 Jul 2024 15:06:26 +0200 Subject: [PATCH 19/26] Get rid of more 'unknown' names in the C frontend --- src/rewrite/vct/rewrite/EncodeArrayValues.scala | 11 +---------- src/rewrite/vct/rewrite/lang/LangCToCol.scala | 5 +++-- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/src/rewrite/vct/rewrite/EncodeArrayValues.scala b/src/rewrite/vct/rewrite/EncodeArrayValues.scala index 9f135261c0..4fa2d90e4a 100644 --- a/src/rewrite/vct/rewrite/EncodeArrayValues.scala +++ b/src/rewrite/vct/rewrite/EncodeArrayValues.scala @@ -171,15 +171,6 @@ case class EncodeArrayValues[Pre <: Generation]() extends Rewriter[Pre] { (p: Expr[Pre]) => PointerInsufficientFreePermission(p), ), ) - var requires = (ptr !== Null()) &* - (PointerBlockOffset(ptr)(FramedPtrBlockOffset) === zero) &* - makeStruct.makePerm( - i => - PointerLocation(PointerAdd(ptr, i.get)(FramedPtrOffset))( - FramedPtrOffset - ), - IteratedPtrInjective, - ) requiresT = if (!typeIsRef(t)) requiresT @@ -216,7 +207,7 @@ case class EncodeArrayValues[Pre <: Generation]() extends Rewriter[Pre] { body = None, requires = requiresPred, decreases = Some(DecreasesClauseNoRecursion[Post]()), - )(o.where("free_" + t.toString)) + )(o.where(name = "free_" + t.toString)) }) (proc, (node: FreePointer[Pre]) => PointerFreeFailed(node, errors)) } diff --git a/src/rewrite/vct/rewrite/lang/LangCToCol.scala b/src/rewrite/vct/rewrite/lang/LangCToCol.scala index 517442ae0a..eca1812fb8 100644 --- a/src/rewrite/vct/rewrite/lang/LangCToCol.scala +++ b/src/rewrite/vct/rewrite/lang/LangCToCol.scala @@ -1042,7 +1042,7 @@ case class LangCToCol[Pre <: Generation](rw: LangSpecificToCol[Pre]) contract = rw.dispatch(decl.decl.contract), pure = pure, inline = inline, - )(AbstractApplicable)(init.o) + )(AbstractApplicable)(init.o.sourceName(info.name)) ) ) case None => @@ -1050,7 +1050,8 @@ case class LangCToCol[Pre <: Generation](rw: LangSpecificToCol[Pre]) if (t.isInstanceOf[TByValueClass[Post]]) { TNonNullPointer(t) } else { t } cGlobalNameSuccessor(RefCGlobalDeclaration(decl, idx)) = rw - .globalDeclarations.declare(new HeapVariable(newT)(init.o)) + .globalDeclarations + .declare(new HeapVariable(newT)(init.o.sourceName(info.name))) } } } From a1773b771ff07eca3bb6bbfb83d3df40e6e01f9e Mon Sep 17 00:00:00 2001 From: Alexander Stekelenburg Date: Thu, 25 Jul 2024 16:18:42 +0200 Subject: [PATCH 20/26] Remove Viper field access from trigger with top-level PointerSubscript or DerefPointer * Gives a 12% performance improvement on Silicon for examples/concepts/c/structs.c --- .../vct/rewrite/adt/ImportPointer.scala | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/rewrite/vct/rewrite/adt/ImportPointer.scala b/src/rewrite/vct/rewrite/adt/ImportPointer.scala index 89c7c4e6c5..a8753a98a4 100644 --- a/src/rewrite/vct/rewrite/adt/ImportPointer.scala +++ b/src/rewrite/vct/rewrite/adt/ImportPointer.scala @@ -286,25 +286,25 @@ case class ImportPointer[Pre <: Generation](importer: ImportADTImporter) Nil, Nil, )(PanicBlame("ptr_deref requires nothing.")) - case other => rewriteDefault(other) + case other => dispatch(other) } } override def postCoerce(e: Expr[Pre]): Expr[Post] = { implicit val o: Origin = e.o e match { -// case f @ Forall(_, triggers, _) => -// f.rewrite(triggers = -// triggers.map(_.map(rewriteTopLevelPointerSubscriptInTrigger)) -// ) -// case s @ Starall(_, triggers, _) => -// s.rewrite(triggers = -// triggers.map(_.map(rewriteTopLevelPointerSubscriptInTrigger)) -// ) -// case e @ Exists(_, triggers, _) => -// e.rewrite(triggers = -// triggers.map(_.map(rewriteTopLevelPointerSubscriptInTrigger)) -// ) + case f @ Forall(_, triggers, _) => + f.rewrite(triggers = + triggers.map(_.map(rewriteTopLevelPointerSubscriptInTrigger)) + ) + case s @ Starall(_, triggers, _) => + s.rewrite(triggers = + triggers.map(_.map(rewriteTopLevelPointerSubscriptInTrigger)) + ) + case e @ Exists(_, triggers, _) => + e.rewrite(triggers = + triggers.map(_.map(rewriteTopLevelPointerSubscriptInTrigger)) + ) case sub @ PointerSubscript(pointer, index) => SilverDeref( obj = From 42aca99b11ea27e815b9b4c52774a7dd406ad6d4 Mon Sep 17 00:00:00 2001 From: Alexander Stekelenburg Date: Wed, 21 Aug 2024 14:48:18 +0200 Subject: [PATCH 21/26] Implement basic pointer casts --- src/rewrite/vct/rewrite/ClassToRef.scala | 301 ++++++++++++++---- .../vct/rewrite/EncodeArrayValues.scala | 109 +------ .../vct/rewrite/adt/ImportPointer.scala | 31 +- .../viper/api/transform/ColToSilver.scala | 12 +- 4 files changed, 275 insertions(+), 178 deletions(-) diff --git a/src/rewrite/vct/rewrite/ClassToRef.scala b/src/rewrite/vct/rewrite/ClassToRef.scala index a606a9a908..78e944ea18 100644 --- a/src/rewrite/vct/rewrite/ClassToRef.scala +++ b/src/rewrite/vct/rewrite/ClassToRef.scala @@ -23,6 +23,11 @@ case object ClassToRef extends RewriterBuilder { private def InstanceOfOrigin: Origin = Origin(Seq(PreferredName(Seq("subtype")), LabelContext("classToRef"))) + private def ValueAdtOrigin: Origin = + Origin(Seq(PreferredName(Seq("Value")), LabelContext("classToRef"))) + + private def CastHelperOrigin: Origin = Origin(Seq(LabelContext("classToRef"))) + case class InstanceNullPreconditionFailed( inner: Blame[InstanceNull], inv: InvokingNode[_], @@ -73,6 +78,15 @@ case class ClassToRef[Pre <: Generation]() extends Rewriter[Pre] { val typeOf: SuccessionMap[Unit, Function[Post]] = SuccessionMap() val instanceOf: SuccessionMap[Unit, Function[Post]] = SuccessionMap() + val valueAdt: SuccessionMap[Unit, AxiomaticDataType[Post]] = SuccessionMap() + val valueAdtTypeArgument: Variable[Post] = + new Variable(TType(TAnyValue()))(ValueAdtOrigin.where(name = "V")) + val valueAsFunctions: mutable.Map[Type[Pre], ADTFunction[Post]] = mutable + .Map() + + val castHelpers: SuccessionMap[Type[Pre], Procedure[Post]] = SuccessionMap() + val castHelperCalls: ScopedStack[mutable.Set[Statement[Post]]] = ScopedStack() + def typeNumber(cls: Class[Pre]): Int = typeNumberStore.getOrElseUpdate(cls, typeNumberStore.size + 1) @@ -141,15 +155,68 @@ case class ClassToRef[Pre <: Generation]() extends Rewriter[Pre] { ) } + private def makeValueAdt: AxiomaticDataType[Post] = { + new AxiomaticDataType[Post]( + valueAsFunctions.values.toSeq, + Seq(valueAdtTypeArgument), + )(ValueAdtOrigin) + } + + // TODO: Also generate value as axioms for arrays once those are properly supported for C/CPP/LLVM + private def makeValueAsFunction( + typeName: String, + t: Type[Post], + ): ADTFunction[Post] = { + new ADTFunction[Post]( + Seq(new Variable(TVar[Post](valueAdtTypeArgument.ref))( + ValueAdtOrigin.where(name = "v") + )), + TNonNullPointer(t), + )(ValueAdtOrigin.where(name = "value_as_" + typeName)) + } + + private def unwrapValueAs( + axiomType: TAxiomatic[Post], + oldT: Type[Pre], + newT: Type[Post], + fieldRef: Ref[Post, ADTFunction[Post]], + )(implicit o: Origin): Seq[ADTAxiom[Post]] = { + (oldT match { + case t: TByValueClass[Pre] => { + // TODO: If there are no fields we should ignore the first field and add the axioms for the second field + t.cls.decl.decls.collectFirst({ case field: InstanceField[Pre] => + unwrapValueAs(axiomType, field.t, dispatch(field.t), fieldRef) + }).getOrElse(Nil) + } + case _ => Nil + }) :+ new ADTAxiom[Post](forall( + axiomType, + body = { a => + InlinePattern(adtFunctionInvocation[Post]( + valueAsFunctions + .getOrElseUpdate(oldT, makeValueAsFunction(oldT.toString, newT)) + .ref, + typeArgs = Some((valueAdt.ref(()), Seq(axiomType))), + args = Seq(a), + )) === Cast( + adtFunctionInvocation(fieldRef, args = Seq(a)), + TypeValue(TNonNullPointer(newT)), + ) + }, + )) + } + override def dispatch(program: Program[Pre]): Program[Rewritten[Pre]] = program.rewrite(declarations = globalDeclarations.collect { program.declarations.foreach(dispatch) - implicit val o: Origin = TypeOfOrigin typeOf(()) = makeTypeOf globalDeclarations.declare(typeOf(())) instanceOf(()) = makeInstanceOf globalDeclarations.declare(instanceOf(())) + if (valueAsFunctions.nonEmpty) { + globalDeclarations.declare(valueAdt.getOrElseUpdate((), makeValueAdt)) + } }._1 ) @@ -309,18 +376,49 @@ case class ClassToRef[Pre <: Generation]() extends Rewriter[Pre] { case cls: ByValueClass[Pre] => implicit val o: Origin = cls.o val axiomType = TAxiomatic[Post](byValClassSucc.ref(cls), Nil) + val classType = cls.classType(Nil) + var valueAsAxioms: Seq[ADTAxiom[Post]] = Seq() val (fieldFunctions, fieldInverses, fieldTypes) = cls.decls.collect { case field: Field[Pre] => - val newT = TNonNullPointer(dispatch(field.t)) + val newT = dispatch(field.t) + val nonnullT = TNonNullPointer(newT) byValFieldSucc(field) = new ADTFunction[Post]( Seq(new Variable(axiomType)(field.o)), - newT, + nonnullT, )(field.o) + if (valueAsAxioms.isEmpty) { + // This is the first field + valueAsAxioms = + valueAsAxioms :+ new ADTAxiom[Post](forall( + axiomType, + body = { a => + InlinePattern(adtFunctionInvocation[Post]( + valueAsFunctions.getOrElseUpdate( + field.t, + makeValueAsFunction(field.t.toString, newT), + ).ref, + typeArgs = Some((valueAdt.ref(()), Seq(axiomType))), + args = Seq(a), + )) === adtFunctionInvocation( + byValFieldSucc.ref(field), + args = Seq(a), + ) + }, + )) + + valueAsAxioms = + valueAsAxioms ++ unwrapValueAs( + axiomType, + field.t, + newT, + byValFieldSucc.ref(field), + ) + } ( byValFieldSucc(field), new ADTFunction[Post]( - Seq(new Variable(newT)(field.o)), + Seq(new Variable(nonnullT)(field.o)), axiomType, )( field.o.copy( @@ -331,7 +429,7 @@ case class ClassToRef[Pre <: Generation]() extends Rewriter[Pre] { .getOrElse("unknown") ) ), - newT, + nonnullT, ) }.unzip3 val constructor = @@ -432,7 +530,8 @@ case class ClassToRef[Pre <: Generation]() extends Rewriter[Pre] { byValClassSucc(cls) = new AxiomaticDataType[Post]( Seq(indexFunction, injectivityAxiom) ++ destructorAxioms ++ - indexAxioms ++ fieldFunctions ++ fieldInverses, + indexAxioms ++ fieldFunctions ++ fieldInverses ++ + valueAsAxioms, Nil, ) globalDeclarations.succeed(cls, byValClassSucc(cls)) @@ -467,55 +566,63 @@ case class ClassToRef[Pre <: Generation]() extends Rewriter[Pre] { } } - override def dispatch(stat: Statement[Pre]): Statement[Post] = - stat match { - case Instantiate(Ref(cls), Local(Ref(v))) => - instantiate(cls, succ(v))(stat.o) - case inv @ InvokeMethod( - obj, - Ref(method), - args, - outArgs, - typeArgs, - givenMap, - yields, - ) => - InvokeProcedure[Post]( - ref = methodSucc.ref(method), - args = dispatch(obj) +: args.map(dispatch), - outArgs = outArgs.map(dispatch), - typeArgs = typeArgs.map(dispatch), - givenMap = givenMap.map { case (Ref(v), e) => - (succ(v), dispatch(e)) - }, - yields = yields.map { case (e, Ref(v)) => (dispatch(e), succ(v)) }, - )(PreBlameSplit.left( - InstanceNullPreconditionFailed(inv.blame, inv), - PreBlameSplit - .left(PanicBlame("incorrect instance method type?"), inv.blame), - ))(inv.o) - case inv @ InvokeConstructor( - Ref(cons), - _, - out, - args, - outArgs, - typeArgs, - givenMap, - yields, - ) => - InvokeProcedure[Post]( - ref = consSucc.ref(cons), - args = args.map(dispatch), - outArgs = dispatch(out) +: outArgs.map(dispatch), - typeArgs = typeArgs.map(dispatch), - givenMap = givenMap.map { case (Ref(v), e) => - (succ(v), dispatch(e)) - }, - yields = yields.map { case (e, Ref(v)) => (dispatch(e), succ(v)) }, - )(inv.blame)(inv.o) - case other => super.dispatch(other) - } + override def dispatch(stat: Statement[Pre]): Statement[Post] = { + val helpers: mutable.Set[Statement[Post]] = mutable.Set() + val result = + castHelperCalls.having(helpers) { + stat match { + case Instantiate(Ref(cls), Local(Ref(v))) => + instantiate(cls, succ(v))(stat.o) + case inv @ InvokeMethod( + obj, + Ref(method), + args, + outArgs, + typeArgs, + givenMap, + yields, + ) => + InvokeProcedure[Post]( + ref = methodSucc.ref(method), + args = dispatch(obj) +: args.map(dispatch), + outArgs = outArgs.map(dispatch), + typeArgs = typeArgs.map(dispatch), + givenMap = givenMap.map { case (Ref(v), e) => + (succ(v), dispatch(e)) + }, + yields = yields.map { case (e, Ref(v)) => (dispatch(e), succ(v)) }, + )(PreBlameSplit.left( + InstanceNullPreconditionFailed(inv.blame, inv), + PreBlameSplit + .left(PanicBlame("incorrect instance method type?"), inv.blame), + ))(inv.o) + case inv @ InvokeConstructor( + Ref(cons), + _, + out, + args, + outArgs, + typeArgs, + givenMap, + yields, + ) => + InvokeProcedure[Post]( + ref = consSucc.ref(cons), + args = args.map(dispatch), + outArgs = dispatch(out) +: outArgs.map(dispatch), + typeArgs = typeArgs.map(dispatch), + givenMap = givenMap.map { case (Ref(v), e) => + (succ(v), dispatch(e)) + }, + yields = yields.map { case (e, Ref(v)) => (dispatch(e), succ(v)) }, + )(inv.blame)(inv.o) + case other => super.dispatch(other) + } + } + + if (helpers.nonEmpty) { Block(helpers.toSeq :+ result)(stat.o) } + else { result } + } override def dispatch(node: ApplyAnyPredicate[Pre]): ApplyAnyPredicate[Post] = node match { @@ -527,6 +634,77 @@ case class ClassToRef[Pre <: Generation]() extends Rewriter[Pre] { case other => other.rewriteDefault() } + private def unwrapCastConstraints(outerType: Type[Post], t: Type[Pre])( + implicit o: Origin + ): Expr[Post] = { + val newT = dispatch(t) + val constraint = forall[Post]( + TNonNullPointer(outerType), + body = { p => + PolarityDependent( + Greater( + CurPerm(PointerLocation(p)(PanicBlame( + "Referring to a non-null pointer should not cause any verification failures" + ))), + NoPerm(), + ) ==> + (InlinePattern(Cast(p, TypeValue(TNonNullPointer(newT)))) === + adtFunctionInvocation( + valueAsFunctions + .getOrElseUpdate(t, makeValueAsFunction(t.toString, newT)) + .ref, + typeArgs = Some((valueAdt.ref(()), Seq(outerType))), + args = Seq(DerefPointer(p)(PanicBlame( + "Pointer deref is safe since the permission is framed" + ))), + )), + tt, + ) + }, + ) + + if (t.isInstanceOf[TByValueClass[Pre]]) { + constraint &* + t.asInstanceOf[TByValueClass[Pre]].cls.decl.decls.collectFirst { + case field: InstanceField[Pre] => + unwrapCastConstraints(outerType, field.t) + }.getOrElse(tt) + } else { constraint } + } + + private def makeCastHelper(t: Type[Pre]): Procedure[Post] = { + implicit val o: Origin = CastHelperOrigin + .where(name = "constraints_" + t.toString) + globalDeclarations.declare(procedure( + AbstractApplicable, + TrueSatisfiable, + ensures = UnitAccountedPredicate(unwrapCastConstraints(dispatch(t), t)), + )) + } + + private def addCastHelpers(t: Type[Pre], calls: mutable.Set[Statement[Post]])( + implicit o: Origin + ): Unit = { + t match { + case cls: TByValueClass[Pre] => { + calls.add( + InvokeProcedure[Post]( + castHelpers.getOrElseUpdate(t, makeCastHelper(t)).ref, + Nil, + Nil, + Nil, + Nil, + Nil, + )(TrueSatisfiable)(o) + ) + cls.cls.decl.decls.collectFirst { case field: InstanceField[Pre] => + addCastHelpers(field.t, calls) + } + } + case _ => + } + } + override def dispatch(e: Expr[Pre]): Expr[Post] = e match { case inv @ MethodInvocation( @@ -640,7 +818,18 @@ case class ClassToRef[Pre <: Generation]() extends Rewriter[Pre] { Nil, Nil, )(PanicBlame("instanceOf requires nothing"))(e.o) - case Cast(value, typeValue) if value.t.asPointer.isEmpty => + case Cast(value, typeValue) if value.t.asPointer.isDefined => { + // Keep pointer casts and add extra annotations + // TODO: Check if we need to get rid of the pointer add's here since in my testing that broke some of the reasoning + if (castHelperCalls.nonEmpty) { + addCastHelpers(value.t.asPointer.get.element, castHelperCalls.top)( + e.o + ) + } + + e.rewriteDefault() + } + case Cast(value, typeValue) => dispatch( value ) // Discard for now, should assert instanceOf(value, typeValue) diff --git a/src/rewrite/vct/rewrite/EncodeArrayValues.scala b/src/rewrite/vct/rewrite/EncodeArrayValues.scala index 4fa2d90e4a..afc4a6b820 100644 --- a/src/rewrite/vct/rewrite/EncodeArrayValues.scala +++ b/src/rewrite/vct/rewrite/EncodeArrayValues.scala @@ -185,8 +185,7 @@ case class EncodeArrayValues[Pre <: Generation]() extends Rewriter[Pre] { // If structure contains structs, the permission for those fields need to be released as well val permFields = t match { - case t: TClass[Pre] => - unwrapStructPerm(access, None, t, o, makeStruct) + case t: TClass[Pre] => unwrapStructPerm(access, t, o, makeStruct) case _ => Seq() } requiresT = @@ -405,55 +404,8 @@ case class EncodeArrayValues[Pre <: Generation]() extends Rewriter[Pre] { })) } - def unwrapStructCasts( - struct: Variable[Post] => Expr[Post], - pointer: Variable[Post] => Expr[Post], - structType: TClass[Pre], - origin: Origin, - makeStruct: MakeAnns, - visited: Seq[TClass[Pre]] = Seq(), - ): Seq[(Expr[Post], Expr[Pre] => PointerFreeError)] = { - if (visited.contains(structType)) { - // We do not allow this notation for recursive structs - throw UnsupportedStructPerm(origin) - } - implicit val o: Origin = origin - val field = structType.cls.decl.declarations.collectFirst { - case field: InstanceField[Pre] => field - } - if (field.isDefined) { - // TODO: I kind of want to only do this one level deep (but for every type) - val fieldType = field.get.t - val result = - if (typeIsRef(fieldType)) { - unwrapStructCasts( - (i: Variable[Post]) => - Deref[Post](struct(i), succ(field.get))(DerefPerm), - pointer, - fieldType.asInstanceOf[TClass[Pre]], - origin, - makeStruct, - structType +: visited, - ) - } else { Nil } - result :+ - (( - makeStruct.makeCast( - pointer, - (i: Variable[Post]) => - AddrOf(Deref[Post](struct(i), succ(field.get))(DerefPerm)), - TNonNullPointer(dispatch(fieldType)), - ), - // Error should never occur since this part should not be emitted in the definition of free - (p: Expr[Pre]) => GenericPointerFreeError(p), - )) - } else { Nil } - } - def unwrapStructPerm( struct: Variable[Post] => Expr[Post], - // Needs to be provided to define asType assertions using casts - pointer: Option[Variable[Post] => Expr[Post]], structType: TClass[Pre], origin: Origin, makeStruct: MakeAnns, @@ -482,18 +434,6 @@ case class EncodeArrayValues[Pre <: Generation]() extends Rewriter[Pre] { Referrable.originName(member), ), )) - if (pointer.isDefined) { - anns = - anns :+ - (( - makeStruct.makeSelfCast( - (i: Variable[Post]) => AddrOf(loc(i)), - TNonNullPointer(dispatch(member.t)), - ), - // Error should never occur since this part should not be emitted in the definition of free - (p: Expr[Pre]) => GenericPointerFreeError(p), - )) - } anns = if (typeIsRef(member.t)) anns :+ @@ -508,7 +448,6 @@ case class EncodeArrayValues[Pre <: Generation]() extends Rewriter[Pre] { // We recurse, since a field is another struct anns ++ unwrapStructPerm( loc, - pointer.map { _ => (i: Variable[Post]) => AddrOf(loc(i)) }, newStruct, origin, makeStruct, @@ -518,17 +457,7 @@ case class EncodeArrayValues[Pre <: Generation]() extends Rewriter[Pre] { } }) - if (pointer.isDefined && fields.nonEmpty) { - // TODO: I kind of want to only do this one level deep (but for every type) - newFieldPerms.flatten ++ unwrapStructCasts( - struct, - pointer.get, - structType, - origin, - makeStruct, - visited, - ) - } else { newFieldPerms.flatten } + newFieldPerms.flatten } case class MakeAnns( @@ -557,29 +486,6 @@ case class EncodeArrayValues[Pre <: Generation]() extends Rewriter[Pre] { val body = (pre1 && pre2 && access(i) === access(j)) ==> (i.get === j.get) Forall(Seq(i, j), Seq(triggerUnique), body) } - - def makeCast( - pointer: Variable[Post] => Expr[Post], - fieldPointer: Variable[Post] => Expr[Post], - t: Type[Post], - ): Expr[Post] = { - implicit val o: Origin = arrayCreationOrigin - val zero = const[Post](0) - val body = (zero <= i.get && i.get < size) ==> - (Cast(pointer(i), TypeValue(t)) === Cast(fieldPointer(i), TypeValue(t))) - Forall(Seq(i), Seq(Seq(Cast(pointer(i), TypeValue(t)))), body) - } - - def makeSelfCast( - pointer: Variable[Post] => Expr[Post], - t: Type[Post], - ): Expr[Post] = { - implicit val o: Origin = arrayCreationOrigin - val zero = const[Post](0) - val body = (zero <= i.get && i.get < size) ==> - (Cast(pointer(i), TypeValue(t)) === pointer(i)) - Forall(Seq(i), Seq(Seq(Cast(pointer(i), TypeValue(t)))), body) - } } def typeIsRef(t: Type[_]): Boolean = @@ -639,15 +545,8 @@ case class EncodeArrayValues[Pre <: Generation]() extends Rewriter[Pre] { val permFields = elementType match { - case t: TClass[Pre] => - unwrapStructPerm(access, Some(pointerAccess), t, o, makeStruct) - case t => - Seq(( - makeStruct - .makeSelfCast(pointerAccess, TNonNullPointer(dispatch(t))), - // Will never be used - (p: Expr[Pre]) => GenericPointerFreeError(p), - )) + case t: TClass[Pre] => unwrapStructPerm(access, t, o, makeStruct) + case _ => Nil } ensures = diff --git a/src/rewrite/vct/rewrite/adt/ImportPointer.scala b/src/rewrite/vct/rewrite/adt/ImportPointer.scala index a8753a98a4..8c807ccb93 100644 --- a/src/rewrite/vct/rewrite/adt/ImportPointer.scala +++ b/src/rewrite/vct/rewrite/adt/ImportPointer.scala @@ -2,6 +2,7 @@ package vct.col.rewrite.adt import vct.col.ast._ import ImportADT.typeText +import hre.util.ScopedStack import vct.col.origin._ import vct.col.ref.Ref import vct.col.rewrite.Generation @@ -90,6 +91,7 @@ case class ImportPointer[Pre <: Generation](importer: ImportADTImporter) : SuccessionMap[Type[Pre], Procedure[Post]] = SuccessionMap() val asTypeFunctions: mutable.Map[Type[Pre], Function[Post]] = mutable.Map() + private val inAxiom: ScopedStack[Unit] = ScopedStack() private def makeAsTypeFunction(typeName: String): Function[Post] = { val value = @@ -186,6 +188,21 @@ case class ImportPointer[Pre <: Generation](importer: ImportADTImporter) case other => super.applyCoercion(e, other) } + override def postCoerce(decl: Declaration[Pre]): Unit = { + decl match { + case axiom: ADTAxiom[Pre] => + inAxiom.having(()) { + allScopes.anySucceed(axiom, axiom.rewriteDefault()) + } + // TODO: This is an ugly way to exempt this one bit of generated code from having ptrAdd's added + case proc: Procedure[Pre] + if proc.o.find[LabelContext].exists(_.label == "classToRef") && + proc.o.getPreferredNameOrElse().snake.startsWith("constraints_") => + inAxiom.having(()) { allScopes.anySucceed(proc, proc.rewriteDefault()) } + case _ => super.postCoerce(decl) + } + } + override def postCoerce(t: Type[Pre]): Type[Post] = t match { case TPointer(_) => TOption(TAxiomatic(pointerAdt.ref, Nil)) @@ -270,7 +287,7 @@ case class ImportPointer[Pre <: Generation](importer: ImportADTImporter) case deref @ DerefPointer(pointer) => FunctionInvocation[Post]( ref = pointerDeref.ref, - args = Seq( + args = Seq(if (inAxiom.isEmpty) { FunctionInvocation[Post]( ref = pointerAdd.ref, // Always index with zero, otherwise quantifiers with pointers do not get triggered @@ -281,7 +298,7 @@ case class ImportPointer[Pre <: Generation](importer: ImportADTImporter) )(NoContext( DerefPointerBoundsPreconditionFailed(deref.blame, pointer) )) - ), + } else { unwrapOption(pointer, deref.blame) }), typeArgs = Nil, Nil, Nil, @@ -343,7 +360,7 @@ case class ImportPointer[Pre <: Generation](importer: ImportADTImporter) obj = FunctionInvocation[Post]( ref = pointerDeref.ref, - args = Seq( + args = Seq(if (inAxiom.isEmpty) { FunctionInvocation[Post]( ref = pointerAdd.ref, // Always index with zero, otherwise quantifiers with pointers do not get triggered @@ -354,7 +371,7 @@ case class ImportPointer[Pre <: Generation](importer: ImportADTImporter) )(NoContext( DerefPointerBoundsPreconditionFailed(deref.blame, pointer) )) - ), + } else { unwrapOption(pointer, deref.blame) }), typeArgs = Nil, Nil, Nil, @@ -364,7 +381,7 @@ case class ImportPointer[Pre <: Generation](importer: ImportADTImporter) case deref @ RawDerefPointer(pointer) => FunctionInvocation[Post]( ref = pointerDeref.ref, - args = Seq( + args = Seq(if (inAxiom.isEmpty) { FunctionInvocation[Post]( ref = pointerAdd.ref, // Always index with zero, otherwise quantifiers with pointers do not get triggered @@ -375,7 +392,7 @@ case class ImportPointer[Pre <: Generation](importer: ImportADTImporter) )(NoContext( DerefPointerBoundsPreconditionFailed(deref.blame, pointer) )) - ), + } else { unwrapOption(pointer, deref.blame) }), typeArgs = Nil, Nil, Nil, @@ -447,6 +464,8 @@ case class ImportPointer[Pre <: Generation](importer: ImportADTImporter) .getOrElseUpdate(innerType, makeAsTypeFunction(innerType.toString)).ref, Seq(preExpr match { case PointerAdd(_, _) => postExpr + // Don't add ptrAdd in an ADT axiom since we cannot use functions with preconditions there + case _ if inAxiom.nonEmpty => postExpr case _ => FunctionInvocation[Post]( ref = pointerAdd.ref, diff --git a/src/viper/viper/api/transform/ColToSilver.scala b/src/viper/viper/api/transform/ColToSilver.scala index a3aaf43270..9e27216415 100644 --- a/src/viper/viper/api/transform/ColToSilver.scala +++ b/src/viper/viper/api/transform/ColToSilver.scala @@ -230,17 +230,7 @@ case class ColToSilver(program: col.Program[_]) { function.contract.decreases.toSeq.map(decreases), accountedPred(function.contract.ensures), function.body.map(exp), - )( - pos = pos(function), - info = - if (ref(function) == "ptrDerefblahblah") - ConsInfo( - AnnotationInfo(Map("opaque" -> Seq())), - NodeInfo(function), - ) - else - NodeInfo(function), - ) + )(pos = pos(function), info = NodeInfo(function)) } case procedure: col.Procedure[_] if procedure.returnType == col.TVoid() && !procedure.inline && From 9b96b334cd5bb59a7e078c505534cb0eb94ee8dd Mon Sep 17 00:00:00 2001 From: Alexander Stekelenburg Date: Thu, 22 Aug 2024 13:59:38 +0200 Subject: [PATCH 22/26] Add pointer cast helpers in loops --- examples/concepts/c/pointer_casts.c | 71 ++++++++++++ src/rewrite/vct/rewrite/ClassToRef.scala | 103 ++++++++++++++---- .../vct/rewrite/adt/ImportPointer.scala | 100 ++++++++++------- .../vct/test/integration/examples/CSpec.scala | 15 +-- 4 files changed, 219 insertions(+), 70 deletions(-) create mode 100644 examples/concepts/c/pointer_casts.c diff --git a/examples/concepts/c/pointer_casts.c b/examples/concepts/c/pointer_casts.c new file mode 100644 index 0000000000..4d5e06ab6e --- /dev/null +++ b/examples/concepts/c/pointer_casts.c @@ -0,0 +1,71 @@ +#include + +struct A { + int integer; + bool boolean; +}; + +struct B { + struct A struct_a; +}; + +void canCastToInteger() { + struct B struct_b; + struct_b.struct_a.integer = 5; + int *pointer_to_integer = (int *)&struct_b; + //@ assert *pointer_to_integer == 5; + //@ assert pointer_to_integer == &struct_b.struct_a.integer; + //@ assert pointer_to_integer == (int *)&struct_b.struct_a; + // The following is not implemented yet + // assert pointer_to_integer == &struct_b + // assert pointer_to_integer == &struct_b.struct_a + *pointer_to_integer = 10; + //@ assert struct_b.struct_a.integer == 10; +} + +void cannotCastToBoolean() { + struct B struct_b; + struct_b.struct_a.boolean = true == true; // We currently don't support boolean literals + // TODO: Do proper type checks for casts + bool *pointer_to_boolean = (bool *)&struct_b; + /*[/expect ptrPerm]*/ + //@ assert *pointer_to_boolean == 5; + /*[/end]*/ + //@ assert pointer_to_boolean == &struct_b.struct_a.boolean; + //@ assert pointer_to_boolean == (bool *)&struct_b.struct_a; +} + +void castRemainsValidInLoop() { + struct B struct_b; + struct_b.struct_a.integer = 10; + + int *pointer_to_integer = (int *)&struct_b; + + //@ loop_invariant 0 <= i && i <= 10; + //@ loop_invariant Perm(&struct_b, write); + //@ loop_invariant Perm(struct_b, write); + //@ loop_invariant pointer_to_integer == (int *)&struct_b; + //@ loop_invariant *pointer_to_integer == 10 - i; + for (int i = 0; i < 10; i++) { + *pointer_to_integer = *pointer_to_integer - 1; + } + + //@ assert struct_b.struct_a.integer == 0; +} + +//@ requires a != NULL; +//@ context Perm(a, write); +//@ ensures *a == \old(*a) + 1; +void increaseByOne(int *a) { + *a += 1; +} + +void callWithCast() { + struct B struct_b; + struct_b.struct_a.integer = 15; + + int *pointer_to_integer = (int *)&struct_b; + increaseByOne(pointer_to_integer); + + //@ assert struct_b.struct_a.integer == 16; +} diff --git a/src/rewrite/vct/rewrite/ClassToRef.scala b/src/rewrite/vct/rewrite/ClassToRef.scala index 78e944ea18..f56042400b 100644 --- a/src/rewrite/vct/rewrite/ClassToRef.scala +++ b/src/rewrite/vct/rewrite/ClassToRef.scala @@ -26,7 +26,8 @@ case object ClassToRef extends RewriterBuilder { private def ValueAdtOrigin: Origin = Origin(Seq(PreferredName(Seq("Value")), LabelContext("classToRef"))) - private def CastHelperOrigin: Origin = Origin(Seq(LabelContext("classToRef"))) + private def CastHelperOrigin: Origin = + Origin(Seq(LabelContext("classToRef cast helpers"))) case class InstanceNullPreconditionFailed( inner: Blame[InstanceNull], @@ -85,7 +86,7 @@ case class ClassToRef[Pre <: Generation]() extends Rewriter[Pre] { .Map() val castHelpers: SuccessionMap[Type[Pre], Procedure[Post]] = SuccessionMap() - val castHelperCalls: ScopedStack[mutable.Set[Statement[Post]]] = ScopedStack() + val requiredCastHelpers: ScopedStack[mutable.Set[Type[Pre]]] = ScopedStack() def typeNumber(cls: Class[Pre]): Int = typeNumberStore.getOrElseUpdate(cls, typeNumberStore.size + 1) @@ -566,10 +567,65 @@ case class ClassToRef[Pre <: Generation]() extends Rewriter[Pre] { } } + private def addCastConstraints( + expr: Expr[Pre], + totalHelpers: mutable.Set[Type[Pre]], + ): Expr[Post] = { + val helpers: mutable.Set[Type[Pre]] = mutable.Set() + var result: Seq[Expr[Post]] = Nil + for (clause <- expr.unfoldStar) { + val newClause = requiredCastHelpers.having(helpers) { dispatch(clause) } + if (helpers.nonEmpty) { + result ++= helpers.map { t => + unwrapCastConstraints(dispatch(t), t)(CastHelperOrigin) + }.toSeq + totalHelpers.addAll(helpers) + helpers.clear() + } + result = result :+ newClause + } + foldStar(result)(expr.o) + } + + // For loops add cast helpers before and as an invariant (since otherwise the contract might not be well-formed) + override def dispatch(node: LoopContract[Pre]): LoopContract[Post] = { + implicit val o: Origin = node.o + val helpers: mutable.Set[Type[Pre]] = mutable.Set() + node match { + case LoopInvariant(invariant, decreases) => { + val result = + LoopInvariant( + addCastConstraints(invariant, helpers), + decreases.map(dispatch), + )(node.o) + if (requiredCastHelpers.nonEmpty) { + requiredCastHelpers.top.addAll(helpers) + } + result + } + case contract @ IterationContract( + requires, + ensures, + context_everywhere, + ) => { + val result = + IterationContract( + addCastConstraints(requires, helpers), + addCastConstraints(ensures, helpers), + addCastConstraints(context_everywhere, helpers), + )(contract.blame)(node.o) + if (requiredCastHelpers.nonEmpty) { + requiredCastHelpers.top.addAll(helpers) + } + result + } + } + } + override def dispatch(stat: Statement[Pre]): Statement[Post] = { - val helpers: mutable.Set[Statement[Post]] = mutable.Set() + val helpers: mutable.Set[Type[Pre]] = mutable.Set() val result = - castHelperCalls.having(helpers) { + requiredCastHelpers.having(helpers) { stat match { case Instantiate(Ref(cls), Local(Ref(v))) => instantiate(cls, succ(v))(stat.o) @@ -620,8 +676,18 @@ case class ClassToRef[Pre <: Generation]() extends Rewriter[Pre] { } } - if (helpers.nonEmpty) { Block(helpers.toSeq :+ result)(stat.o) } - else { result } + if (helpers.nonEmpty) { + Block(helpers.map { t => + InvokeProcedure[Post]( + castHelpers.getOrElseUpdate(t, makeCastHelper(t)).ref, + Nil, + Nil, + Nil, + Nil, + Nil, + )(TrueSatisfiable)(CastHelperOrigin) + }.toSeq :+ result)(stat.o) + } else { result } } override def dispatch(node: ApplyAnyPredicate[Pre]): ApplyAnyPredicate[Post] = @@ -682,23 +748,15 @@ case class ClassToRef[Pre <: Generation]() extends Rewriter[Pre] { )) } - private def addCastHelpers(t: Type[Pre], calls: mutable.Set[Statement[Post]])( - implicit o: Origin + private def addCastHelpers( + t: Type[Pre], + helpers: mutable.Set[Type[Pre]], ): Unit = { t match { case cls: TByValueClass[Pre] => { - calls.add( - InvokeProcedure[Post]( - castHelpers.getOrElseUpdate(t, makeCastHelper(t)).ref, - Nil, - Nil, - Nil, - Nil, - Nil, - )(TrueSatisfiable)(o) - ) + helpers.add(t) cls.cls.decl.decls.collectFirst { case field: InstanceField[Pre] => - addCastHelpers(field.t, calls) + addCastHelpers(field.t, helpers) } } case _ => @@ -820,11 +878,8 @@ case class ClassToRef[Pre <: Generation]() extends Rewriter[Pre] { )(PanicBlame("instanceOf requires nothing"))(e.o) case Cast(value, typeValue) if value.t.asPointer.isDefined => { // Keep pointer casts and add extra annotations - // TODO: Check if we need to get rid of the pointer add's here since in my testing that broke some of the reasoning - if (castHelperCalls.nonEmpty) { - addCastHelpers(value.t.asPointer.get.element, castHelperCalls.top)( - e.o - ) + if (requiredCastHelpers.nonEmpty) { + addCastHelpers(value.t.asPointer.get.element, requiredCastHelpers.top) } e.rewriteDefault() diff --git a/src/rewrite/vct/rewrite/adt/ImportPointer.scala b/src/rewrite/vct/rewrite/adt/ImportPointer.scala index 8c807ccb93..72b8432099 100644 --- a/src/rewrite/vct/rewrite/adt/ImportPointer.scala +++ b/src/rewrite/vct/rewrite/adt/ImportPointer.scala @@ -196,8 +196,8 @@ case class ImportPointer[Pre <: Generation](importer: ImportADTImporter) } // TODO: This is an ugly way to exempt this one bit of generated code from having ptrAdd's added case proc: Procedure[Pre] - if proc.o.find[LabelContext].exists(_.label == "classToRef") && - proc.o.getPreferredNameOrElse().snake.startsWith("constraints_") => + if proc.o.find[LabelContext] + .exists(_.label == "classToRef cast helpers") => inAxiom.having(()) { allScopes.anySucceed(proc, proc.rewriteDefault()) } case _ => super.postCoerce(decl) } @@ -287,18 +287,24 @@ case class ImportPointer[Pre <: Generation](importer: ImportADTImporter) case deref @ DerefPointer(pointer) => FunctionInvocation[Post]( ref = pointerDeref.ref, - args = Seq(if (inAxiom.isEmpty) { - FunctionInvocation[Post]( - ref = pointerAdd.ref, - // Always index with zero, otherwise quantifiers with pointers do not get triggered - args = Seq(unwrapOption(pointer, deref.blame), const(0)), - typeArgs = Nil, - Nil, - Nil, - )(NoContext( - DerefPointerBoundsPreconditionFailed(deref.blame, pointer) - )) - } else { unwrapOption(pointer, deref.blame) }), + args = Seq( + if ( + inAxiom.isEmpty && + !deref.o.find[LabelContext] + .exists(_.label == "classToRef cast helpers") + ) { + FunctionInvocation[Post]( + ref = pointerAdd.ref, + // Always index with zero, otherwise quantifiers with pointers do not get triggered + args = Seq(unwrapOption(pointer, deref.blame), const(0)), + typeArgs = Nil, + Nil, + Nil, + )(NoContext( + DerefPointerBoundsPreconditionFailed(deref.blame, pointer) + )) + } else { unwrapOption(pointer, deref.blame) } + ), typeArgs = Nil, Nil, Nil, @@ -360,18 +366,24 @@ case class ImportPointer[Pre <: Generation](importer: ImportADTImporter) obj = FunctionInvocation[Post]( ref = pointerDeref.ref, - args = Seq(if (inAxiom.isEmpty) { - FunctionInvocation[Post]( - ref = pointerAdd.ref, - // Always index with zero, otherwise quantifiers with pointers do not get triggered - args = Seq(unwrapOption(pointer, deref.blame), const(0)), - typeArgs = Nil, - Nil, - Nil, - )(NoContext( - DerefPointerBoundsPreconditionFailed(deref.blame, pointer) - )) - } else { unwrapOption(pointer, deref.blame) }), + args = Seq( + if ( + inAxiom.isEmpty && + !deref.o.find[LabelContext] + .exists(_.label == "classToRef cast helpers") + ) { + FunctionInvocation[Post]( + ref = pointerAdd.ref, + // Always index with zero, otherwise quantifiers with pointers do not get triggered + args = Seq(unwrapOption(pointer, deref.blame), const(0)), + typeArgs = Nil, + Nil, + Nil, + )(NoContext( + DerefPointerBoundsPreconditionFailed(deref.blame, pointer) + )) + } else { unwrapOption(pointer, deref.blame) } + ), typeArgs = Nil, Nil, Nil, @@ -381,18 +393,24 @@ case class ImportPointer[Pre <: Generation](importer: ImportADTImporter) case deref @ RawDerefPointer(pointer) => FunctionInvocation[Post]( ref = pointerDeref.ref, - args = Seq(if (inAxiom.isEmpty) { - FunctionInvocation[Post]( - ref = pointerAdd.ref, - // Always index with zero, otherwise quantifiers with pointers do not get triggered - args = Seq(unwrapOption(pointer, deref.blame), const(0)), - typeArgs = Nil, - Nil, - Nil, - )(NoContext( - DerefPointerBoundsPreconditionFailed(deref.blame, pointer) - )) - } else { unwrapOption(pointer, deref.blame) }), + args = Seq( + if ( + inAxiom.isEmpty && + !deref.o.find[LabelContext] + .exists(_.label == "classToRef cast helpers") + ) { + FunctionInvocation[Post]( + ref = pointerAdd.ref, + // Always index with zero, otherwise quantifiers with pointers do not get triggered + args = Seq(unwrapOption(pointer, deref.blame), const(0)), + typeArgs = Nil, + Nil, + Nil, + )(NoContext( + DerefPointerBoundsPreconditionFailed(deref.blame, pointer) + )) + } else { unwrapOption(pointer, deref.blame) } + ), typeArgs = Nil, Nil, Nil, @@ -465,7 +483,11 @@ case class ImportPointer[Pre <: Generation](importer: ImportADTImporter) Seq(preExpr match { case PointerAdd(_, _) => postExpr // Don't add ptrAdd in an ADT axiom since we cannot use functions with preconditions there - case _ if inAxiom.nonEmpty => postExpr + case _ + if inAxiom.nonEmpty || + !preExpr.o.find[LabelContext] + .exists(_.label == "classToRef cast helpers") => + postExpr case _ => FunctionInvocation[Post]( ref = pointerAdd.ref, diff --git a/test/main/vct/test/integration/examples/CSpec.scala b/test/main/vct/test/integration/examples/CSpec.scala index 9526f29bbe..d234f8ef0a 100644 --- a/test/main/vct/test/integration/examples/CSpec.scala +++ b/test/main/vct/test/integration/examples/CSpec.scala @@ -11,6 +11,7 @@ class CSpec extends VercorsSpec { vercors should verify using silicon example "concepts/c/structs.c" vercors should verify using silicon example "concepts/c/vector_add.c" vercors should verify using silicon example "concepts/c/vector_type.c" + vercors should verify using silicon example "concepts/c/pointer_casts.c" vercors should error withCode "resolutionError:type" in "float should not be demoted" c """ @@ -377,17 +378,17 @@ class CSpec extends VercorsSpec { #include struct nested { - struct nested *inner; + struct nested *inner; }; void main() { - int *ip = NULL; - double *dp = NULL; - struct nested *np = NULL; - np = (struct nested*) NULL; + int *ip = NULL; + double *dp = NULL; + struct nested *np = NULL; + np = (struct nested*) NULL; np = (struct nested*) malloc(sizeof(struct nested)); np->inner = NULL; - np->inner = (struct nested*) NULL; + np->inner = (struct nested*) NULL; } """ @@ -562,4 +563,4 @@ class CSpec extends VercorsSpec { return; } """ -} \ No newline at end of file +} From 800f1ddae5dadb193e9ed90f36557d65c7212ab3 Mon Sep 17 00:00:00 2001 From: Alexander Stekelenburg Date: Thu, 22 Aug 2024 15:23:18 +0200 Subject: [PATCH 23/26] Add type checking for pointer casts --- examples/concepts/c/pointer_casts.c | 41 ++++++++++++++----- src/col/vct/col/typerules/CoercionUtils.scala | 12 ++++++ src/rewrite/vct/rewrite/ClassToRef.scala | 33 +++++++-------- src/rewrite/vct/rewrite/lang/LangCToCol.scala | 12 +++++- .../vct/test/integration/examples/CSpec.scala | 9 ++++ 5 files changed, 78 insertions(+), 29 deletions(-) diff --git a/examples/concepts/c/pointer_casts.c b/examples/concepts/c/pointer_casts.c index 4d5e06ab6e..d09264f7aa 100644 --- a/examples/concepts/c/pointer_casts.c +++ b/examples/concepts/c/pointer_casts.c @@ -23,17 +23,6 @@ void canCastToInteger() { //@ assert struct_b.struct_a.integer == 10; } -void cannotCastToBoolean() { - struct B struct_b; - struct_b.struct_a.boolean = true == true; // We currently don't support boolean literals - // TODO: Do proper type checks for casts - bool *pointer_to_boolean = (bool *)&struct_b; - /*[/expect ptrPerm]*/ - //@ assert *pointer_to_boolean == 5; - /*[/end]*/ - //@ assert pointer_to_boolean == &struct_b.struct_a.boolean; - //@ assert pointer_to_boolean == (bool *)&struct_b.struct_a; -} void castRemainsValidInLoop() { struct B struct_b; @@ -50,6 +39,36 @@ void castRemainsValidInLoop() { *pointer_to_integer = *pointer_to_integer - 1; } + //@ assert struct_b.struct_a.integer == 0; + struct_b.struct_a.integer = 10; + + // We can also specify the permission through the pointer + //@ loop_invariant 0 <= i && i <= 10; + //@ loop_invariant Perm(pointer_to_integer, write); + //@ loop_invariant *pointer_to_integer == 10 - i; + for (int i = 0; i < 10; i++) { + *pointer_to_integer = *pointer_to_integer - 1; + } + + //@ assert struct_b.struct_a.integer == 0; +} + +void castRemainsValidInParBlock() { + struct B struct_b; + struct_b.struct_a.integer = 10; + + int *pointer_to_integer = (int *)&struct_b; + + //@ context i == 8 ==> Perm(pointer_to_integer, write); + //@ ensures i == 8 ==> *pointer_to_integer == 0; + for (int i = 0; i < 10; i++) { + if (i == 8) { + *pointer_to_integer = *pointer_to_integer - 10; + } + } + + // Unfortunately we don't support a par block where we specify permission to the struct and then access through the cast (the generated cast helper is put too far away) + //@ assert struct_b.struct_a.integer == 0; } diff --git a/src/col/vct/col/typerules/CoercionUtils.scala b/src/col/vct/col/typerules/CoercionUtils.scala index 5c16be6dd6..ca97d5ef3f 100644 --- a/src/col/vct/col/typerules/CoercionUtils.scala +++ b/src/col/vct/col/typerules/CoercionUtils.scala @@ -449,6 +449,18 @@ case object CoercionUtils { case _ => None } + def firstElementIsType[G](aggregate: Type[G], innerType: Type[G]): Boolean = + aggregate match { + case aggregate if getAnyCoercion(aggregate, innerType).isDefined => true + case clazz: TByValueClass[G] => + clazz.cls.decl.decls.collectFirst { case field: InstanceField[G] => + firstElementIsType(field.t, innerType) + }.getOrElse(false) + case TArray(element) => firstElementIsType(element, innerType) + // TODO: Add LLVM types + case _ => false + } + def getAnyCArrayCoercion[G]( source: Type[G] ): Option[(Coercion[G], CTArray[G])] = diff --git a/src/rewrite/vct/rewrite/ClassToRef.scala b/src/rewrite/vct/rewrite/ClassToRef.scala index f56042400b..240edb33be 100644 --- a/src/rewrite/vct/rewrite/ClassToRef.scala +++ b/src/rewrite/vct/rewrite/ClassToRef.scala @@ -603,22 +603,23 @@ case class ClassToRef[Pre <: Generation]() extends Rewriter[Pre] { } result } - case contract @ IterationContract( - requires, - ensures, - context_everywhere, - ) => { - val result = - IterationContract( - addCastConstraints(requires, helpers), - addCastConstraints(ensures, helpers), - addCastConstraints(context_everywhere, helpers), - )(contract.blame)(node.o) - if (requiredCastHelpers.nonEmpty) { - requiredCastHelpers.top.addAll(helpers) - } - result - } +// case contract @ IterationContract( +// requires, +// ensures, +// context_everywhere, +// ) => { +// val result = +// IterationContract( +// addCastConstraints(requires, helpers), +// addCastConstraints(ensures, helpers), +// addCastConstraints(context_everywhere, helpers), +// )(contract.blame)(node.o) +// if (requiredCastHelpers.nonEmpty) { +// requiredCastHelpers.top.addAll(helpers) +// } +// result +// } + case _: IterationContract[Pre] => throw ExtraNode } } diff --git a/src/rewrite/vct/rewrite/lang/LangCToCol.scala b/src/rewrite/vct/rewrite/lang/LangCToCol.scala index eca1812fb8..3b98839ed6 100644 --- a/src/rewrite/vct/rewrite/lang/LangCToCol.scala +++ b/src/rewrite/vct/rewrite/lang/LangCToCol.scala @@ -14,6 +14,7 @@ import vct.col.resolve.ctx._ import vct.col.resolve.lang.C.nameFromDeclarator import vct.col.resolve.lang.Java.logger import vct.col.rewrite.{Generation, Rewritten} +import vct.col.typerules.CoercionUtils import vct.col.typerules.CoercionUtils.getCoercion import vct.col.util.SuccessionMap import vct.col.util.AstBuildHelpers._ @@ -412,9 +413,16 @@ case class LangCToCol[Pre <: Generation](rw: LangSpecificToCol[Pre]) case CCast(CInvocation(CLocal("__vercors_malloc"), _, _, _), _) => throw UnsupportedMalloc(c) case CCast(n @ Null(), t) if t.asPointer.isDefined => rw.dispatch(n) - // TODO: Check if valid pointer cast case CCast(e, t) if e.t.asPointer.isDefined && t.asPointer.isDefined => - Cast(rw.dispatch(e), TypeValue(rw.dispatch(t))(t.o))(c.o) + val newE = rw.dispatch(e) + val newT = rw.dispatch(t) + if ( + CoercionUtils.firstElementIsType( + newE.t.asPointer.get.element, + newT.asPointer.get.element, + ) + ) { Cast(newE, TypeValue(newT)(t.o))(c.o) } + else { throw UnsupportedCast(c) } case _ => throw UnsupportedCast(c) } diff --git a/test/main/vct/test/integration/examples/CSpec.scala b/test/main/vct/test/integration/examples/CSpec.scala index d234f8ef0a..943fcc7085 100644 --- a/test/main/vct/test/integration/examples/CSpec.scala +++ b/test/main/vct/test/integration/examples/CSpec.scala @@ -563,4 +563,13 @@ class CSpec extends VercorsSpec { return; } """ + + vercors should error withCode "unsupportedCast" in "Casting struct pointers only works for the first element" c + """ + void cannotCastToBoolean() { + struct B struct_b; + struct_b.struct_a.boolean = true == true; // We currently don't support boolean literals + bool *pointer_to_boolean = (bool *)&struct_b; + } + """ } From c7a723ebf7e364eb22e161c08a5da5ebe2988605 Mon Sep 17 00:00:00 2001 From: Alexander Stekelenburg Date: Thu, 22 Aug 2024 16:27:02 +0200 Subject: [PATCH 24/26] Add back blame erroneously removed by the previous commit --- src/rewrite/vct/rewrite/ClassToRef.scala | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/rewrite/vct/rewrite/ClassToRef.scala b/src/rewrite/vct/rewrite/ClassToRef.scala index 240edb33be..0b084bacbb 100644 --- a/src/rewrite/vct/rewrite/ClassToRef.scala +++ b/src/rewrite/vct/rewrite/ClassToRef.scala @@ -589,15 +589,14 @@ case class ClassToRef[Pre <: Generation]() extends Rewriter[Pre] { // For loops add cast helpers before and as an invariant (since otherwise the contract might not be well-formed) override def dispatch(node: LoopContract[Pre]): LoopContract[Post] = { - implicit val o: Origin = node.o val helpers: mutable.Set[Type[Pre]] = mutable.Set() node match { - case LoopInvariant(invariant, decreases) => { + case inv @ LoopInvariant(invariant, decreases) => { val result = LoopInvariant( addCastConstraints(invariant, helpers), decreases.map(dispatch), - )(node.o) + )(inv.blame)(node.o) if (requiredCastHelpers.nonEmpty) { requiredCastHelpers.top.addAll(helpers) } From f0257061f1b00deb377a0210ad7c9b73fe62cbf7 Mon Sep 17 00:00:00 2001 From: Alexander Stekelenburg Date: Mon, 26 Aug 2024 10:45:43 +0200 Subject: [PATCH 25/26] Fix unsupported cast test --- test/main/vct/test/integration/examples/CSpec.scala | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/main/vct/test/integration/examples/CSpec.scala b/test/main/vct/test/integration/examples/CSpec.scala index 943fcc7085..02abb280ad 100644 --- a/test/main/vct/test/integration/examples/CSpec.scala +++ b/test/main/vct/test/integration/examples/CSpec.scala @@ -566,6 +566,15 @@ class CSpec extends VercorsSpec { vercors should error withCode "unsupportedCast" in "Casting struct pointers only works for the first element" c """ + #include + struct A { + int integer; + bool boolean; + }; + + struct B { + struct A struct_a; + }; void cannotCastToBoolean() { struct B struct_b; struct_b.struct_a.boolean = true == true; // We currently don't support boolean literals From d85b6b472e2aaf390f54b92af9e0a90b62b8d278 Mon Sep 17 00:00:00 2001 From: Alexander Stekelenburg Date: Fri, 13 Sep 2024 13:41:42 +0200 Subject: [PATCH 26/26] Fix unsoundness in pointer cast encoding --- src/rewrite/vct/rewrite/ClassToRef.scala | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/rewrite/vct/rewrite/ClassToRef.scala b/src/rewrite/vct/rewrite/ClassToRef.scala index 0b084bacbb..bdb3f6e026 100644 --- a/src/rewrite/vct/rewrite/ClassToRef.scala +++ b/src/rewrite/vct/rewrite/ClassToRef.scala @@ -377,7 +377,6 @@ case class ClassToRef[Pre <: Generation]() extends Rewriter[Pre] { case cls: ByValueClass[Pre] => implicit val o: Origin = cls.o val axiomType = TAxiomatic[Post](byValClassSucc.ref(cls), Nil) - val classType = cls.classType(Nil) var valueAsAxioms: Seq[ADTAxiom[Post]] = Seq() val (fieldFunctions, fieldInverses, fieldTypes) = cls.decls.collect { case field: Field[Pre] => @@ -409,12 +408,21 @@ case class ClassToRef[Pre <: Generation]() extends Rewriter[Pre] { )) valueAsAxioms = - valueAsAxioms ++ unwrapValueAs( - axiomType, - field.t, - newT, - byValFieldSucc.ref(field), - ) + valueAsAxioms ++ + (field.t match { + case t: TByValueClass[Pre] => + // TODO: If there are no fields we should ignore the first field and add the axioms for the second field + t.cls.decl.decls + .collectFirst({ case innerF: InstanceField[Pre] => + unwrapValueAs( + axiomType, + innerF.t, + dispatch(innerF.t), + byValFieldSucc.ref(field), + ) + }).getOrElse(Nil) + case _ => Nil + }) } ( byValFieldSucc(field),