Skip to content

Commit

Permalink
Merge pull request #2586 from onflow/supun/builtin-entitlements
Browse files Browse the repository at this point in the history
Introduce built-in mutability entitlements
  • Loading branch information
SupunS authored Jul 6, 2023
2 parents 2a08798 + 21c47ab commit 3a298b0
Show file tree
Hide file tree
Showing 12 changed files with 327 additions and 20 deletions.
23 changes: 21 additions & 2 deletions runtime/interpreter/interpreter.go
Original file line number Diff line number Diff line change
Expand Up @@ -4945,7 +4945,22 @@ func (interpreter *Interpreter) GetPathCapabilityFinalTarget(
}

func (interpreter *Interpreter) getEntitlement(typeID common.TypeID) (*sema.EntitlementType, error) {
location, _, _ := common.DecodeTypeID(interpreter, string(typeID))
location, qualifiedIdentifier, err := common.DecodeTypeID(interpreter, string(typeID))
if err != nil {
return nil, err
}

if location == nil {
ty := sema.BuiltinEntitlements[qualifiedIdentifier]
if ty == nil {
return nil, TypeLoadingError{
TypeID: typeID,
}
}

return ty, nil
}

elaboration := interpreter.getElaboration(location)
if elaboration == nil {
return nil, TypeLoadingError{
Expand All @@ -4964,7 +4979,11 @@ func (interpreter *Interpreter) getEntitlement(typeID common.TypeID) (*sema.Enti
}

func (interpreter *Interpreter) getEntitlementMapType(typeID common.TypeID) (*sema.EntitlementMapType, error) {
location, _, _ := common.DecodeTypeID(interpreter, string(typeID))
location, _, err := common.DecodeTypeID(interpreter, string(typeID))
if err != nil {
return nil, err
}

elaboration := interpreter.getElaboration(location)
if elaboration == nil {
return nil, TypeLoadingError{
Expand Down
6 changes: 6 additions & 0 deletions runtime/sema/entitlements.cdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@

entitlement Mutable

entitlement Insertable

entitlement Removable
41 changes: 41 additions & 0 deletions runtime/sema/entitlements.gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 21 additions & 0 deletions runtime/sema/entitlements.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
* Cadence - The resource-oriented smart contract programming language
*
* Copyright Dapper Labs, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package sema

//go:generate go run ./gen entitlements.cdc entitlements.gen.go
113 changes: 108 additions & 5 deletions runtime/sema/gen/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -573,9 +573,19 @@ func (*generator) VisitTransactionDeclaration(_ *ast.TransactionDeclaration) str
panic("transaction declarations are not supported")
}

func (*generator) VisitEntitlementDeclaration(_ *ast.EntitlementDeclaration) struct{} {
// TODO
panic("entitlement declarations are not supported")
func (g *generator) VisitEntitlementDeclaration(decl *ast.EntitlementDeclaration) (_ struct{}) {
entitlementName := decl.Identifier.Identifier
typeVarName := entitlementVarName(entitlementName)
typeVarDecl := entitlementTypeLiteral(entitlementName)

g.addDecls(
goVarDecl(
typeVarName,
typeVarDecl,
),
)

return
}

func (*generator) VisitEntitlementMappingDeclaration(_ *ast.EntitlementMappingDeclaration) struct{} {
Expand Down Expand Up @@ -1006,6 +1016,70 @@ func (g *generator) currentMemberID(memberName string) string {
return b.String()
}

func (g *generator) generateTypeInit(program *ast.Program) {

// Currently this only generate registering of entitlements.
// It is possible to extend this to register other types as well.
// So they are not needed to be manually added to the base activation.

/* Generates the following:
func init() {
BuiltinEntitlements[Foo.Identifier] = Foo
addToBaseActivation(Foo)
...
}
*/

if len(program.EntitlementDeclarations()) == 0 {
return
}

stmts := make([]dst.Stmt, 0)

for _, declaration := range program.EntitlementDeclarations() {
const entitlementsName = "BuiltinEntitlements"
varName := entitlementVarName(declaration.Identifier.Identifier)

mapUpdateStmt := &dst.AssignStmt{
Lhs: []dst.Expr{
&dst.IndexExpr{
X: dst.NewIdent(entitlementsName),
Index: &dst.SelectorExpr{
X: dst.NewIdent(varName),
Sel: dst.NewIdent("Identifier"),
},
},
},
Tok: token.ASSIGN,
Rhs: []dst.Expr{
dst.NewIdent(varName),
},
}

typeRegisterStmt := &dst.ExprStmt{
X: &dst.CallExpr{
Fun: dst.NewIdent("addToBaseActivation"),
Args: []dst.Expr{
dst.NewIdent(varName),
},
},
}

stmts = append(stmts, mapUpdateStmt, typeRegisterStmt)
}

initDecl := &dst.FuncDecl{
Name: dst.NewIdent("init"),
Type: &dst.FuncType{},
Body: &dst.BlockStmt{
List: stmts,
},
}

g.addDecls(initDecl)
}

func goField(name string, ty dst.Expr) *dst.Field {
return &dst.Field{
Names: []*dst.Ident{
Expand Down Expand Up @@ -1087,6 +1161,10 @@ func typeVarName(typeName string) string {
return fmt.Sprintf("%sType", typeName)
}

func entitlementVarName(typeName string) string {
return fmt.Sprintf("%sEntitlement", typeName)
}

func typeVarIdent(typeName string) *dst.Ident {
return dst.NewIdent(typeVarName(typeName))
}
Expand Down Expand Up @@ -1499,6 +1577,24 @@ func typeParameterExpr(name string, typeBound dst.Expr) dst.Expr {
}
}

func entitlementTypeLiteral(name string) dst.Expr {
// &sema.EntitlementType{
// Identifier: "Foo",
//}

elements := []dst.Expr{
goKeyValue("Identifier", goStringLit(name)),
}

return &dst.UnaryExpr{
Op: token.AND,
X: &dst.CompositeLit{
Type: dst.NewIdent("EntitlementType"),
Elts: elements,
},
}
}

func parseCadenceFile(path string) *ast.Program {
program, code, err := parser.ParseProgramFromFile(nil, path, parserConfig)
if err != nil {
Expand All @@ -1513,7 +1609,7 @@ func parseCadenceFile(path string) *ast.Program {
return program
}

func gen(inPath string, outFile *os.File) {
func gen(inPath string, outFile *os.File, registerTypes bool) {
program := parseCadenceFile(inPath)

var gen generator
Expand All @@ -1522,6 +1618,10 @@ func gen(inPath string, outFile *os.File) {
_ = ast.AcceptDeclaration[struct{}](declaration, &gen)
}

if registerTypes {
gen.generateTypeInit(program)
}

writeGoFile(inPath, outFile, gen.decls)
}

Expand Down Expand Up @@ -1561,5 +1661,8 @@ func main() {
}
defer outFile.Close()

gen(inPath, outFile)
// Register generated test types in base activation.
const registerTypes = true

gen(inPath, outFile, registerTypes)
}
5 changes: 4 additions & 1 deletion runtime/sema/gen/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,10 @@ func TestFiles(t *testing.T) {
require.NoError(t, err)
defer outFile.Close()

gen(inputPath, outFile)
// Do not register generated test types in base activation.
const registerTypes = false

gen(inputPath, outFile, registerTypes)

goldenPath := filepath.Join(testDataDirectory, testname+".golden.go")
want, err := os.ReadFile(goldenPath)
Expand Down
1 change: 1 addition & 0 deletions runtime/sema/gen/testdata/entitlement.cdc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
entitlement Foo
24 changes: 24 additions & 0 deletions runtime/sema/gen/testdata/entitlement.golden.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

30 changes: 18 additions & 12 deletions runtime/sema/type.go
Original file line number Diff line number Diff line change
Expand Up @@ -3455,18 +3455,7 @@ func init() {
)

for _, ty := range types {
typeName := ty.String()

// Check that the type is not accidentally redeclared

if BaseTypeActivation.Find(typeName) != nil {
panic(errors.NewUnreachableError())
}

BaseTypeActivation.Set(
typeName,
baseTypeVariable(typeName, ty),
)
addToBaseActivation(ty)
}

// The AST contains empty type annotations, resolve them to Void
Expand All @@ -3477,6 +3466,21 @@ func init() {
)
}

func addToBaseActivation(ty Type) {
typeName := ty.String()

// Check that the type is not accidentally redeclared

if BaseTypeActivation.Find(typeName) != nil {
panic(errors.NewUnreachableError())
}

BaseTypeActivation.Set(
typeName,
baseTypeVariable(typeName, ty),
)
}

func baseTypeVariable(name string, ty Type) *Variable {
return &Variable{
Identifier: name,
Expand Down Expand Up @@ -3554,6 +3558,8 @@ var AllNumberTypes = append(
SignedNumberType,
)

var BuiltinEntitlements = map[string]*EntitlementType{}

const NumberTypeMinFieldName = "min"
const NumberTypeMaxFieldName = "max"

Expand Down
16 changes: 16 additions & 0 deletions runtime/sema/type_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -798,7 +798,13 @@ func TestCommonSuperType(t *testing.T) {
var tests []testCase

err := BaseTypeActivation.ForEach(func(name string, variable *Variable) error {
// Entitlements are not typical types. So skip.
if _, ok := BuiltinEntitlements[name]; ok {
return nil
}

typ := variable.Type

tests = append(tests, testCase{
name: name,
types: []Type{
Expand Down Expand Up @@ -1890,6 +1896,11 @@ func TestTypeInclusions(t *testing.T) {
t.Parallel()

err := BaseTypeActivation.ForEach(func(name string, variable *Variable) error {
// Entitlements are not typical types. So skip.
if _, ok := BuiltinEntitlements[name]; ok {
return nil
}

t.Run(name, func(t *testing.T) {

typ := variable.Type
Expand All @@ -1910,6 +1921,11 @@ func TestTypeInclusions(t *testing.T) {
t.Parallel()

err := BaseTypeActivation.ForEach(func(name string, variable *Variable) error {
// Entitlements are not typical types. So skip.
if _, ok := BuiltinEntitlements[name]; ok {
return nil
}

t.Run(name, func(t *testing.T) {

typ := variable.Type
Expand Down
Loading

0 comments on commit 3a298b0

Please sign in to comment.