Skip to content

Commit

Permalink
Introduce external trait decorators and implement rudimentary externa…
Browse files Browse the repository at this point in the history
…l struct system
  • Loading branch information
Matthew Ross Rachar committed Sep 2, 2019
1 parent 7d14c58 commit 956a427
Show file tree
Hide file tree
Showing 24 changed files with 302 additions and 131 deletions.
6 changes: 3 additions & 3 deletions Sources/AST/Declaration/TraitDeclaration.swift
Expand Up @@ -14,20 +14,20 @@ public struct TraitDeclaration: ASTNode {
public var identifier: Identifier
// TODO: public var states: [TypeState]
public var members: [TraitMember]
public var moduleAddress: String? // Move-specific
public var decorators: [FunctionCall] // Move-specific

public init(
traitKind: Token,
traitToken: Token,
identifier: Identifier,
members: [TraitMember],
moduleAddress: String? = nil
decorators: [FunctionCall] = []
) {
self.traitKind = traitKind
self.traitToken = traitToken
self.identifier = identifier
self.members = members
self.moduleAddress = moduleAddress
self.decorators = decorators
}

// MARK: - ASTNode
Expand Down
3 changes: 3 additions & 0 deletions Sources/AST/Environment/Environment+Additions.swift
Expand Up @@ -263,6 +263,9 @@ extension Environment {
let special = externalTraitInitializer(trait)
addInitializerSignature(special, enclosingType: trait.identifier.name, callerProtections: [],
generated: true)
if !trait.decorators.isEmpty {
types[trait.identifier.name]?.decorators = trait.decorators
}
} else {
isExternal = false
}
Expand Down
Expand Up @@ -10,7 +10,7 @@ public enum CallableInformation {
case functionInformation(FunctionInformation)
case specialInformation(SpecialInformation)

var parameterTypes: [RawType] {
public var parameterTypes: [RawType] {
switch self {
case .functionInformation(let functionInformation):
return functionInformation.parameterTypes
Expand Down
1 change: 1 addition & 0 deletions Sources/AST/Environment/Information/TypeInformation.swift
Expand Up @@ -16,6 +16,7 @@ public struct TypeInformation {
public var publicInitializer: SpecialDeclaration?
var publicFallback: SpecialDeclaration?
var conformances: [TypeInformation] = []
public var decorators: [FunctionCall]?

public var allFunctions: [String: [FunctionInformation]] {
return conformances.map({ $0.functions }).reduce(functions, +)
Expand Down
18 changes: 17 additions & 1 deletion Sources/Compiler/Compiler.swift
Expand Up @@ -765,6 +765,16 @@ public enum CompilerTarget {
case .move: return "mvir"
}
}

public func configureSemanticAnalyser(semanticAnalyser: SemanticAnalyzer) -> SemanticAnalyzer {
switch self {
case .evm: return semanticAnalyser
case .move:
var semanticAnalyser = semanticAnalyser
semanticAnalyser.allowExternalStructs = true
return semanticAnalyser
}
}
}

public struct CompilerConfiguration {
Expand Down Expand Up @@ -814,7 +824,13 @@ public struct CompilerConfiguration {
self.skipVerifier = skipVerifier
self.skipCodeGen = skipCodeGen
self.diagnostics = diagnostics //Compiler.defaultASTPasses
self.astPasses = astPasses ?? (Compiler.defaultASTPasses + (skipVerifier ? [] : Compiler.verifierASTPasses))
self.astPasses = (astPasses ?? (Compiler.defaultASTPasses + (skipVerifier ? [] : Compiler.verifierASTPasses)))
.map { (pass: ASTPass) in
if let pass = pass as? SemanticAnalyzer {
return target.configureSemanticAnalyser(semanticAnalyser: pass)
}
return pass
}
self.stdLib = stdLib
self.target = target
}
Expand Down
37 changes: 37 additions & 0 deletions Sources/MoveGen/AST/TraitDeclaration.swift
@@ -0,0 +1,37 @@
//
// Created by matthewross on 31/08/19.
//

import Foundation
import AST

extension TraitDeclaration {
var moduleAddress: String? {
guard let argument: FunctionArgument = decorators.first(where: { $0.identifier.name == "module" })?.arguments[0],
let name = argument.identifier?.name,
name == "address",
case .literal(let token) = argument.expression,
case .literal(.address(let address)) = token.kind else {
return nil
}
return address
}

var isModule: Bool {
return decorators.contains(where: { $0.identifier.name == "module" })
}

var associatedModule: String? {
guard let argument = decorators.first(where: { $0.identifier.name == "associated" })?.arguments[0],
let name = argument.identifier?.name,
name == "",
case .identifier(let identifier) = argument.expression else {
return nil
}
return identifier.name
}

var isStruct: Bool {
return decorators.contains(where: { $0.identifier.name == "data" })
}
}
6 changes: 4 additions & 2 deletions Sources/MoveGen/AST/Type.swift
Expand Up @@ -8,15 +8,17 @@
import AST

extension RawType {
public func isExternalTraitType(environment: Environment) -> Bool {
public func isExternalContract(environment: Environment) -> Bool {
var internalRawType: RawType = self
while case .inoutType(let inoutType) = internalRawType {
internalRawType = inoutType
}
if case .userDefinedType(let typeIdentifier) = internalRawType {
return environment.isExternalTraitDeclared(typeIdentifier)
&& !(environment.types[typeIdentifier].flatMap { (information: TypeInformation) in
information.decorators?.contains(where: { $0.identifier.name == "data" })
} ?? false)
}

return false
}
}
29 changes: 12 additions & 17 deletions Sources/MoveGen/Expression/MoveExternalCall.swift
Expand Up @@ -21,23 +21,18 @@ struct MoveExternalCall {
var lookupCall = functionCall
// remove external contract address from lookup
lookupCall.arguments.removeFirst()
guard case .matchedFunction(let matchingFunction) =
functionContext.environment.matchFunctionCall(lookupCall,
enclosingType: functionCall.identifier.enclosingType ??
functionContext.enclosingTypeName,
typeStates: [],
callerProtections: [],
scopeContext: functionContext.scopeContext) else {
fatalError("cannot match function signature in external call")
}

matchingFunction.parameterTypes.forEach { paremeterType in
switch paremeterType {
case .basicType, .externalType:
break
default:
fatalError("cannot use non-basic type in external call")
}
let matched = functionContext.environment.matchFunctionCall(lookupCall,
enclosingType: functionCall.identifier.enclosingType ??
functionContext.enclosingTypeName,
typeStates: [],
callerProtections: [],
scopeContext: functionContext.scopeContext)
if case .matchedFunction = matched {
} else if case .failure(let candidates) = matched,
let candidate: CallableInformation = candidates.first,
case .functionInformation = candidate {
} else {
fatalError("cannot match function signature `\(lookupCall)' in external call")
}

switch externalCall.mode {
Expand Down
21 changes: 15 additions & 6 deletions Sources/MoveGen/Expression/MoveFunctionCall.swift
Expand Up @@ -37,18 +37,27 @@ struct MoveFunctionCall {
lookupCall.arguments.remove(at: 0)
}

if environment.isExternalTraitInitializer(functionCall: lookupCall) {
let externalContractAddress = lookupCall.arguments[0].expression
return MoveExpression(expression: externalContractAddress, position: .normal)
.rendered(functionContext: functionContext)
var moduleName = self.moduleName
var callName = functionCall.mangledIdentifier ?? functionCall.identifier.name

if environment.isExternalTraitInitializer(functionCall: lookupCall),
let trait: TypeInformation = environment.types[lookupCall.identifier.name] {
if trait.decorators?.contains(where: { $0.identifier.name == "data" }) ?? false {
moduleName = lookupCall.identifier.name
callName = "new"
} else {
let externalContractAddress = lookupCall.arguments[0].expression
return MoveExpression(expression: externalContractAddress, position: .normal)
.rendered(functionContext: functionContext)
}
}

let args: [MoveIR.Expression] = functionCall.arguments.map({ (argument: FunctionArgument) in
return MoveExpression(expression: argument.expression, position: .normal)
MoveExpression(expression: argument.expression, position: .normal)
.rendered(functionContext: functionContext)
})

let identifier = "\(moduleName).\(functionCall.mangledIdentifier ?? functionCall.identifier.name)"
let identifier = "\(moduleName).\(callName)"
return .functionCall(MoveIR.FunctionCall(identifier, args))
}
}
40 changes: 31 additions & 9 deletions Sources/MoveGen/MoveCanonicalType.swift
Expand Up @@ -17,6 +17,7 @@ indirect enum CanonicalType: CustomStringConvertible {
case bytearray
case resource(String)
case `struct`(String)
case external(String, CanonicalType)
// case reference(CanonicalType) Not Yet Used
case mutableReference(CanonicalType)

Expand All @@ -34,16 +35,25 @@ indirect enum CanonicalType: CustomStringConvertible {
}

case .userDefinedType(let identifier):
if rawType.isCurrencyType || (environment?.isContractDeclared(identifier) ?? false) {
if let environment = environment,
CanonicalType.isResourceType(rawType, identifier: identifier, environment: environment) {
self = .resource(identifier)
} else if environment?.isExternalTraitDeclared(identifier) ?? false {
} else if let environment = environment,
rawType.isExternalContract(environment: environment) {
self = .address
} else if environment?.isExternalTraitDeclared(identifier) ?? false,
let type: TypeInformation = environment?.types[identifier] {
if type.decorators?.contains(where: { $0.identifier.name == "resource" }) ?? false {
self = .external(identifier, .resource("T"))
} else {
self = .external(identifier, .`struct`("T"))
}
} else if let environment = environment,
environment.isEnumDeclared(rawType.name),
let type: TypeInformation = environment.types[rawType.name],
let value: AST.Expression = type.properties.values.first?.property.value {
environment.isEnumDeclared(identifier),
let type: TypeInformation = environment.types[identifier],
let value: AST.Expression = type.properties.values.first?.property.value {
self = CanonicalType(from: environment.type(of: value,
enclosingType: rawType.name,
enclosingType: identifier,
scopeContext: ScopeContext()))!
} else {
self = .struct(identifier)
Expand All @@ -65,15 +75,22 @@ indirect enum CanonicalType: CustomStringConvertible {
}
}

private static func isResourceType(_ rawType: AST.RawType,
identifier: RawTypeIdentifier,
environment: Environment) -> Bool {
return rawType.isCurrencyType || environment.isContractDeclared(identifier)
}

public var description: String {
switch self {
case .address: return "CanonicalType.address"
case .u64: return "CanonicalType.u64"
case .bool: return "CanonicalType.bool"
case .bytearray: return "CanonicalType.bytearray"
case .resource(let name): return "CanonicalType.R#\(name)"
case .struct(let name): return "CanonicalType.V#\(name)"
case .resource(let name): return "CanonicalType.Resource.\(name)"
case .struct(let name): return "CanonicalType.Struct.\(name)"
case .mutableReference(let type): return "CanonicalType.&mut \(type)"
case .external(let module, let type): return "CanonicalType.External(\(module), \(type))"
}
}

Expand All @@ -91,7 +108,12 @@ indirect enum CanonicalType: CustomStringConvertible {
return .resource(name: "\(name).T")
case .mutableReference(let type):
return .mutableReference(to: type.render(functionContext: functionContext))
case .external(let module, let type):
switch type {
case .resource(let name): return .resource(name: "\(module).\(name)")
case .`struct`(let name): return .`struct`(name: "\(module).\(name)")
default: fatalError("Only external structs and resources are allowed")
}
}

}
}
19 changes: 15 additions & 4 deletions Sources/MoveGen/MoveContract.swift
Expand Up @@ -13,14 +13,21 @@ import MoveIR
struct MoveContract {

static var stateVariablePrefix = "flintState$"
static var reentrancyProtectorValue = 10000

var contractDeclaration: ContractDeclaration
var contractBehaviorDeclarations: [ContractBehaviorDeclaration]
var structDeclarations: [StructDeclaration]
var environment: Environment
var externalTraitDeclarations: [TraitDeclaration]

var externalModules: [TraitDeclaration] {
return externalTraitDeclarations.filter { $0.isModule }
}

var externalStructs: [TraitDeclaration] {
return externalTraitDeclarations.filter { $0.isStruct }
}

init(contractDeclaration: ContractDeclaration,
contractBehaviorDeclarations: [ContractBehaviorDeclaration],
structDeclarations: [StructDeclaration],
Expand All @@ -34,12 +41,16 @@ struct MoveContract {
}

func rendered() -> String {
let imports: [MoveIR.Statement] = externalTraitDeclarations
.map { .`import`(ModuleImport(name: $0.identifier.name, address: $0.moduleAddress!)) }
let imports: [MoveIR.Statement] = externalTraitDeclarations.compactMap { (declaration: TraitDeclaration) in
declaration.moduleAddress.map {
.`import`(ModuleImport(name: declaration.identifier.name, address: $0))
}
}
let renderedImports = MoveIR.Statement.renderStatements(statements: imports)

// Generate code for each function in the contract.
let functions = contractBehaviorDeclarations.flatMap { contractBehaviorDeclaration in
return contractBehaviorDeclaration.members.compactMap { member -> MoveFunction? in
contractBehaviorDeclaration.members.compactMap { member -> MoveFunction? in
guard case .functionDeclaration(let functionDeclaration) = member else {
return nil
}
Expand Down
4 changes: 2 additions & 2 deletions Sources/MoveGen/Preprocessor/MovePreprocessor.swift
Expand Up @@ -37,7 +37,7 @@ public struct MovePreprocessor: ASTPass {
public func process(type: Type, passContext: ASTPassContext) -> ASTPassResult<Type> {
// Used to turn external trait types into addresses
var type = type
if let environment = passContext.environment, type.rawType.isExternalTraitType(environment: environment) {
if let environment = passContext.environment, type.rawType.isExternalContract(environment: environment) {
type.rawType = RawType.externalTraitType
}
return ASTPassResult(element: type, diagnostics: [], passContext: passContext)
Expand Down Expand Up @@ -111,7 +111,7 @@ extension ASTPass {
var type: RawType = environment.type(of: element,
enclosingType: enclosingType,
scopeContext: scopeContext)
if type.isExternalTraitType(environment: environment) {
if type.isExternalContract(environment: environment) {
type = RawType.externalTraitType
}

Expand Down

0 comments on commit 956a427

Please sign in to comment.