Skip to content

Commit

Permalink
Merge pull request #2257 from onflow/bastian/extended-transaction-for…
Browse files Browse the repository at this point in the history
…mat-2

Type check transaction role declarations
  • Loading branch information
turbolent authored Jan 24, 2023
2 parents 67589aa + bb6beba commit fa2c9fe
Show file tree
Hide file tree
Showing 23 changed files with 1,383 additions and 331 deletions.
4 changes: 4 additions & 0 deletions runtime/ast/visitor.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ type StatementDeclarationVisitor[T any] interface {
VisitCompositeDeclaration(*CompositeDeclaration) T
VisitInterfaceDeclaration(*InterfaceDeclaration) T
VisitTransactionDeclaration(*TransactionDeclaration) T
VisitTransactionRoleDeclaration(*TransactionRoleDeclaration) T
}

type DeclarationVisitor[T any] interface {
Expand Down Expand Up @@ -78,6 +79,9 @@ func AcceptDeclaration[T any](declaration Declaration, visitor DeclarationVisito

case ElementTypeTransactionDeclaration:
return visitor.VisitTransactionDeclaration(declaration.(*TransactionDeclaration))

case ElementTypeTransactionRoleDeclaration:
return visitor.VisitTransactionRoleDeclaration(declaration.(*TransactionRoleDeclaration))
}

panic(errors.NewUnreachableError())
Expand Down
4 changes: 4 additions & 0 deletions runtime/common/declarationkind.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,8 @@ func (k DeclarationKind) Name() string {
return "self"
case DeclarationKindTransaction:
return "transaction"
case DeclarationKindTransactionRole:
return "role"
case DeclarationKindPrepare:
return "prepare"
case DeclarationKindExecute:
Expand Down Expand Up @@ -175,6 +177,8 @@ func (k DeclarationKind) Keywords() string {
return "self"
case DeclarationKindTransaction:
return "transaction"
case DeclarationKindTransactionRole:
return "role"
case DeclarationKindPrepare:
return "prepare"
case DeclarationKindExecute:
Expand Down
5 changes: 5 additions & 0 deletions runtime/compiler/compiler.go
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,11 @@ func (compiler *Compiler) VisitTransactionDeclaration(_ *ast.TransactionDeclarat
panic(errors.NewUnreachableError())
}

func (compiler *Compiler) VisitTransactionRoleDeclaration(_ *ast.TransactionRoleDeclaration) ir.Stmt {
// TODO
panic(errors.NewUnreachableError())
}

func (compiler *Compiler) VisitEnumCaseDeclaration(_ *ast.EnumCaseDeclaration) ir.Stmt {
// TODO
panic(errors.NewUnreachableError())
Expand Down
5 changes: 5 additions & 0 deletions runtime/interpreter/interpreter_transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,3 +146,8 @@ func (interpreter *Interpreter) declareTransactionEntryPoint(declaration *ast.Tr
transactionFunction,
)
}

func (interpreter *Interpreter) VisitTransactionRoleDeclaration(_ *ast.TransactionRoleDeclaration) StatementResult {
// TODO:
panic("TODO")
}
147 changes: 147 additions & 0 deletions runtime/parser/declaration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3258,6 +3258,153 @@ func TestParseTransactionDeclaration(t *testing.T) {
)
})

t.Run("field with access, prepare", func(t *testing.T) {
const code = `
transaction {
priv var x: Int
var y: String
prepare(signer: AuthAccount) {
x = 0
}
}
`
result, errs := testParseProgram(code)
require.Empty(t, errs)

utils.AssertEqualWithDiff(t,
[]ast.Declaration{
&ast.TransactionDeclaration{
Fields: []*ast.FieldDeclaration{
{
Access: ast.AccessPrivate,
VariableKind: ast.VariableKindVariable,
Identifier: ast.Identifier{
Identifier: "x",
Pos: ast.Position{Offset: 35, Line: 4, Column: 15},
},
TypeAnnotation: &ast.TypeAnnotation{
IsResource: false,
Type: &ast.NominalType{
Identifier: ast.Identifier{
Identifier: "Int",
Pos: ast.Position{Offset: 38, Line: 4, Column: 18},
},
},
StartPos: ast.Position{Offset: 38, Line: 4, Column: 18},
},
Range: ast.Range{
StartPos: ast.Position{Offset: 26, Line: 4, Column: 6},
EndPos: ast.Position{Offset: 40, Line: 4, Column: 20},
},
},
{
Access: ast.AccessNotSpecified,
VariableKind: ast.VariableKindVariable,
Identifier: ast.Identifier{
Identifier: "y",
Pos: ast.Position{Offset: 58, Line: 5, Column: 16},
},
TypeAnnotation: &ast.TypeAnnotation{
IsResource: false,
Type: &ast.NominalType{
Identifier: ast.Identifier{
Identifier: "String",
Pos: ast.Position{Offset: 61, Line: 5, Column: 19},
},
},
StartPos: ast.Position{Offset: 61, Line: 5, Column: 19},
},
Range: ast.Range{
StartPos: ast.Position{Offset: 54, Line: 5, Column: 12},
EndPos: ast.Position{Offset: 66, Line: 5, Column: 24},
},
},
},
Prepare: &ast.SpecialFunctionDeclaration{
Kind: common.DeclarationKindPrepare,
FunctionDeclaration: &ast.FunctionDeclaration{
Access: ast.AccessNotSpecified,
Identifier: ast.Identifier{
Identifier: "prepare",
Pos: ast.Position{Offset: 75, Line: 7, Column: 6},
},
ParameterList: &ast.ParameterList{
Parameters: []*ast.Parameter{
{
Label: "",
Identifier: ast.Identifier{
Identifier: "signer",
Pos: ast.Position{Offset: 83, Line: 7, Column: 14},
},
TypeAnnotation: &ast.TypeAnnotation{
IsResource: false,
Type: &ast.NominalType{
Identifier: ast.Identifier{
Identifier: "AuthAccount",
Pos: ast.Position{Offset: 91, Line: 7, Column: 22},
},
},
StartPos: ast.Position{Offset: 91, Line: 7, Column: 22},
},
StartPos: ast.Position{Offset: 83, Line: 7, Column: 14},
},
},
Range: ast.Range{
StartPos: ast.Position{Offset: 82, Line: 7, Column: 13},
EndPos: ast.Position{Offset: 102, Line: 7, Column: 33},
},
},
ReturnTypeAnnotation: nil,
FunctionBlock: &ast.FunctionBlock{
Block: &ast.Block{
Statements: []ast.Statement{
&ast.AssignmentStatement{
Target: &ast.IdentifierExpression{
Identifier: ast.Identifier{
Identifier: "x",
Pos: ast.Position{Offset: 117, Line: 8, Column: 11},
},
},
Transfer: &ast.Transfer{
Operation: ast.TransferOperationCopy,
Pos: ast.Position{Offset: 119, Line: 8, Column: 13},
},
Value: &ast.IntegerExpression{
PositiveLiteral: []byte("0"),
Value: new(big.Int),
Base: 10,
Range: ast.Range{
StartPos: ast.Position{Offset: 121, Line: 8, Column: 15},
EndPos: ast.Position{Offset: 121, Line: 8, Column: 15},
},
},
},
},
Range: ast.Range{
StartPos: ast.Position{Offset: 104, Line: 7, Column: 35},
EndPos: ast.Position{Offset: 126, Line: 9, Column: 3},
},
},
PreConditions: nil,
PostConditions: nil,
},
StartPos: ast.Position{Offset: 75, Line: 7, Column: 6},
},
},
PreConditions: nil,
PostConditions: nil,
Range: ast.Range{
StartPos: ast.Position{Offset: 5, Line: 2, Column: 4},
EndPos: ast.Position{Offset: 132, Line: 10, Column: 4},
},
},
},
result.Declarations(),
)
})

t.Run("field, prepare, pre, execute, post", func(t *testing.T) {
const code = `
transaction {
Expand Down
54 changes: 24 additions & 30 deletions runtime/parser/transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,9 @@ func parseTransactionPrepare(p *parser) (*ast.SpecialFunctionDeclaration, error)
}

func parseTransactionFields(p *parser) (fields []*ast.FieldDeclaration, err error) {
access := ast.AccessNotSpecified
var accessPos *ast.Position

for {
_, docString := p.parseTrivia(triviaOptions{
skipNewlines: true,
Expand All @@ -238,19 +241,35 @@ func parseTransactionFields(p *parser) (fields []*ast.FieldDeclaration, err erro
case keywordLet, keywordVar:
field, err := parseFieldWithVariableKind(
p,
ast.AccessNotSpecified,
nil,
access,
accessPos,
nil,
nil,
docString,
)
if err != nil {
return nil, err
}
access = ast.AccessNotSpecified
accessPos = nil

fields = append(fields, field)
continue

case keywordPriv, keywordPub, keywordAccess:
if access != ast.AccessNotSpecified {
return nil, p.syntaxError("invalid second access modifier")
}
pos := p.current.StartPos
accessPos = &pos
var err error
access, err = parseAccess(p)
if err != nil {
return nil, err
}

continue

default:
return
}
Expand Down Expand Up @@ -320,34 +339,9 @@ func parseTransactionRole(p *parser, docString string) (*ast.TransactionRoleDecl
}

// Fields
var fields []*ast.FieldDeclaration
for {
_, docString := p.parseTrivia(triviaOptions{
skipNewlines: true,
parseDocStrings: true,
})

if p.current.Is(lexer.TokenIdentifier) {
switch string(p.currentTokenSource()) {
case keywordLet, keywordVar:
field, err := parseFieldWithVariableKind(
p,
ast.AccessNotSpecified,
nil,
nil,
nil,
docString,
)
if err != nil {
return nil, err
}

fields = append(fields, field)
continue
}
}

break
fields, err := parseTransactionFields(p)
if err != nil {
return nil, err
}

// Prepare (optional)
Expand Down
2 changes: 1 addition & 1 deletion runtime/sema/check_array_expression.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ func (checker *Checker) VisitArrayExpression(expression *ast.ArrayExpression) Ty

argumentTypes[i] = valueType

checker.checkVariableMove(value)
checker.checkValueMove(value)
checker.checkResourceMoveOperation(value, valueType)
}
}
Expand Down
5 changes: 4 additions & 1 deletion runtime/sema/check_assignment.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ func (checker *Checker) checkAssignment(
}
}

checker.checkVariableMove(value)
checker.checkValueMove(value)

checker.recordResourceInvalidation(
value,
Expand Down Expand Up @@ -131,13 +131,16 @@ func (checker *Checker) accessedSelfMember(expression ast.Expression) *Member {
}

var members *StringMemberOrderedMap
// TODO: refactor
switch containerType := variable.Type.(type) {
case *CompositeType:
members = containerType.Members
case *InterfaceType:
members = containerType.Members
case *TransactionType:
members = containerType.Members
case *TransactionRoleType:
members = containerType.Members
default:
panic(errors.NewUnreachableError())
}
Expand Down
22 changes: 6 additions & 16 deletions runtime/sema/check_composite_declaration.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,8 @@ func (checker *Checker) visitCompositeDeclaration(declaration *ast.CompositeDecl
panic(errors.NewUnreachableError())
}

checker.containerTypes[compositeType] = true
defer func() {
checker.containerTypes[compositeType] = false
}()
checker.containerTypes[compositeType] = struct{}{}
defer delete(checker.containerTypes, compositeType)

checker.checkDeclarationAccessModifier(
declaration.Access,
Expand Down Expand Up @@ -77,22 +75,14 @@ func (checker *Checker) visitCompositeDeclaration(declaration *ast.CompositeDecl

var initializationInfo *InitializationInfo

if kind == ContainerKindComposite {
if kind == ContainerKindComposite &&
declaration.CompositeKind != common.CompositeKindEnum {

// The initializer must initialize all members that are fields,
// e.g. not composite functions (which are by definition constant and "initialized")

fields := declaration.Members.Fields()
fieldMembers := orderedmap.New[MemberFieldDeclarationOrderedMap](len(fields))

for _, field := range fields {
fieldName := field.Identifier.Identifier
member, ok := compositeType.Members.Get(fieldName)
if !ok {
continue
}

fieldMembers.Set(member, field)
}
fieldMembers := getFieldMembers(compositeType.Members, fields)

initializationInfo = NewInitializationInfo(compositeType, fieldMembers)
}
Expand Down
2 changes: 1 addition & 1 deletion runtime/sema/check_create_expression.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ func (checker *Checker) checkResourceCreationOrDestruction(compositeType *Compos
return
}
} else {
if checker.containerTypes[contractType] {
if _, ok := checker.containerTypes[contractType]; ok {
return
}
}
Expand Down
4 changes: 2 additions & 2 deletions runtime/sema/check_dictionary_expression.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,11 @@ func (checker *Checker) VisitDictionaryExpression(expression *ast.DictionaryExpr
// not combined after both type checks!

entryKeyType := checker.VisitExpression(entry.Key, keyType)
checker.checkVariableMove(entry.Key)
checker.checkValueMove(entry.Key)
checker.checkResourceMoveOperation(entry.Key, entryKeyType)

entryValueType := checker.VisitExpression(entry.Value, valueType)
checker.checkVariableMove(entry.Value)
checker.checkValueMove(entry.Value)
checker.checkResourceMoveOperation(entry.Value, entryValueType)

entryTypes[i] = DictionaryEntryType{
Expand Down
Loading

0 comments on commit fa2c9fe

Please sign in to comment.