Skip to content

Commit

Permalink
Give an error when trying to substitute only a contract name for a ty…
Browse files Browse the repository at this point in the history
…pe parameter that requires both a contract name and a permission.
  • Loading branch information
mcoblenz committed Jan 21, 2020
1 parent c5bbb81 commit 6d7ecd5
Show file tree
Hide file tree
Showing 5 changed files with 25 additions and 2 deletions.
5 changes: 5 additions & 0 deletions resources/tests/type_checker_tests/GenericsStateVariables.obs
Original file line number Diff line number Diff line change
Expand Up @@ -71,5 +71,10 @@ main contract C {
// This should also fail, because peek() returns an Unowned reference
return maybeA.peek().getX();
}

transaction missingPermission() {
// Error: missing parameter s (Maybe[X@s])
Maybe[A] m = new Maybe[A](new A(4));
}
}

1 change: 1 addition & 0 deletions src/main/scala/edu/cmu/cs/obsidian/parser/AST.scala
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ case class ReturnExpr(e: Expression) extends Statement {
}

// We distinguish between no update clause given and an empty update clause for a clean separation between syntax and semantics.
// thisPermission is the permission of 'this' at the time of the transition. It is used for enforcing state locks caused by dynamic state checks.
case class Transition(newStateName: String, updates: Option[Seq[(ReferenceIdentifier, Expression)]], thisPermission: Permission) extends Statement {
override def substitute(genericParams: Seq[GenericType], actualParams: Seq[ObsidianType]): Transition = {
def doSubstitute: ((ReferenceIdentifier, Expression)) => (ReferenceIdentifier, Expression) = {
Expand Down
13 changes: 11 additions & 2 deletions src/main/scala/edu/cmu/cs/obsidian/typecheck/Checker.scala
Original file line number Diff line number Diff line change
Expand Up @@ -529,6 +529,15 @@ class Checker(globalTable: SymbolTable, verbose: Boolean = false) {
if (isSubtype(table, actualArg, substitutedParam, isThis = false).isDefined) {
logError(ast, GenericParameterError(param, actualArg))
}

actualArg match {
case nonPrimitiveActualArg: NonPrimitiveType =>
if (nonPrimitiveActualArg.permission == Inferred() && param.gVar.permissionVar.isDefined) {
logError(ast, GenericParameterPermissionMissingError(param, param.gVar.permissionVar.get, nonPrimitiveActualArg))
}
case _ =>
// Nothing to do here.
}
}
}

Expand Down Expand Up @@ -2678,7 +2687,7 @@ class Checker(globalTable: SymbolTable, verbose: Boolean = false) {
constr.copy(body = newBody)
}

private def checkConstructors(constructors: Seq[Constructor], contract: ObsidianContractImpl, table: ContractTable): Unit = {
private def checkConstructorDistinguishibility(constructors: Seq[Constructor], contract: ObsidianContractImpl, table: ContractTable): Unit = {
if (constructors.isEmpty && table.stateLookup.nonEmpty && !contract.isInterface) {
logError(contract, NoConstructorError(contract.name))
}
Expand Down Expand Up @@ -2873,7 +2882,7 @@ class Checker(globalTable: SymbolTable, verbose: Boolean = false) {

implementOk(table, obsContract, obsContract.bound.contractName, obsContract.declarations, boundDecls)
newDecls = obsContract.declarations.map(checkDeclaration(table))
checkConstructors(table.constructors, obsContract, table)
checkConstructorDistinguishibility(table.constructors, obsContract, table)
}

obsContract.copy(declarations = newDecls)
Expand Down
4 changes: 4 additions & 0 deletions src/main/scala/edu/cmu/cs/obsidian/typecheck/Error.scala
Original file line number Diff line number Diff line change
Expand Up @@ -437,6 +437,10 @@ case class GenericParameterAssetError(paramName: String, argTypeName: String) ex
s"Argument type $argTypeName is an asset, but the type parameter $paramName is not allowed to be an asset. Add the 'asset' modifier to $paramName to allow the parameter to be an asset."
}

case class GenericParameterPermissionMissingError(param: GenericType, permVariable: String, actualArg: ObsidianType) extends Error {
val msg: String = s"Missing specification for a value for the permission variable $permVariable in $param in type parameter $actualArg."
}

case class PermissionCheckRedundant(actualPerm: Permission, testPermission: Permission,
isSubperm: Boolean) extends Error {
private val isSubpermStr: String =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -871,6 +871,10 @@ class TypeCheckerTests extends JUnitSuite {
(ReceiverTypeIncompatibleError("getX",
ContractReferenceType(ContractType("A", Nil), Unowned(), NotRemoteReferenceType()),
StateType(ContractType("A", Nil), "S1", NotRemoteReferenceType())), 72) ::
(GenericParameterPermissionMissingError(GenericType(GenericVar(isAsset = false,"X",Some("s")),
GenericBoundPerm(interfaceSpecified = false, permSpecified = false, ContractType.topContractType, Unowned())),
"s",
ContractReferenceType(ContractType("A", Nil), Inferred(), NotRemoteReferenceType())), 77)::
Nil)
}

Expand Down

0 comments on commit 6d7ecd5

Please sign in to comment.