From 67f637395c87032f33d239ed25f728c4fd94fdb5 Mon Sep 17 00:00:00 2001 From: Franklin Schrans Date: Wed, 25 Apr 2018 21:27:14 +0100 Subject: [PATCH 1/7] Support inits during parsing --- Sources/AST/AST.swift | 50 +++++++++++- Sources/AST/ASTDumper.swift | 78 ++++++++++++------- Sources/AST/ASTPass.swift | 24 +++++- Sources/AST/ASTVisitor.swift | 45 ++++++++++- Sources/AST/ASTVisitorContext.swift | 2 +- Sources/AST/Token.swift | 2 + Sources/IRGen/IULIAContract.swift | 6 +- Sources/IRGen/IULIAInterface.swift | 10 ++- Sources/IRGen/IULIAPreprocessor.swift | 16 ++++ Sources/Optimizer/Optimizer.swift | 16 ++++ Sources/Parser/Parser.swift | 63 +++++++++++---- Sources/Parser/Tokenizer.swift | 1 + .../SemanticAnalyzer/SemanticAnalyzer.swift | 16 ++++ Sources/SemanticAnalyzer/TypeChecker.swift | 16 ++++ Tests/ParserTests/structs.flint | 8 ++ Tests/ParserTests/types.flint | 11 +++ docs/grammar.txt | 12 ++- 17 files changed, 317 insertions(+), 59 deletions(-) diff --git a/Sources/AST/AST.swift b/Sources/AST/AST.swift index 0557b6f8..0fb4d613 100644 --- a/Sources/AST/AST.swift +++ b/Sources/AST/AST.swift @@ -45,24 +45,33 @@ public struct ContractDeclaration: SourceEntity { } } +/// A member in a contract behavior declaration. +/// +/// - functionDeclaration: The declaration of a function. +/// - initializerDeclaration: The declaration of an initializer. +public enum ContractBehaviorMember: Equatable { + case functionDeclaration(FunctionDeclaration) + case initializerDeclaration(InitializerDeclaration) +} + /// A Flint contract behavior declaration, i.e. the functions of a contract for a given caller capability group. public struct ContractBehaviorDeclaration: SourceEntity { public var contractIdentifier: Identifier public var capabilityBinding: Identifier? public var callerCapabilities: [CallerCapability] - public var functionDeclarations: [FunctionDeclaration] + public var members: [ContractBehaviorMember] public var closeBracketToken: Token public var sourceLocation: SourceLocation { return .spanning(contractIdentifier, to: closeBracketToken) } - public init(contractIdentifier: Identifier, capabilityBinding: Identifier?, callerCapabilities: [CallerCapability], closeBracketToken: Token, functionDeclarations: [FunctionDeclaration]) { + public init(contractIdentifier: Identifier, capabilityBinding: Identifier?, callerCapabilities: [CallerCapability], closeBracketToken: Token, members: [ContractBehaviorMember]) { self.contractIdentifier = contractIdentifier self.capabilityBinding = capabilityBinding self.callerCapabilities = callerCapabilities - self.functionDeclarations = functionDeclarations self.closeBracketToken = closeBracketToken + self.members = members } } @@ -73,6 +82,7 @@ public struct ContractBehaviorDeclaration: SourceEntity { public enum StructMember: Equatable { case variableDeclaration(VariableDeclaration) case functionDeclaration(FunctionDeclaration) + case initializerDeclaration(InitializerDeclaration) } /// The declaration of a struct. @@ -208,6 +218,40 @@ public struct FunctionDeclaration: SourceEntity { } } +public struct InitializerDeclaration: SourceEntity { + public var initToken: Token + + /// The attributes associated with the function, such as `@payable`. + public var attributes: [Attribute] + + /// The modifiers associted with the function, such as `public`. + public var modifiers: [Token] + public var parameters: [Parameter] + public var closeBracketToken: Token + public var body: [Statement] + public var closeBraceToken: Token + + public var sourceLocation: SourceLocation { + return initToken.sourceLocation + } + + /// A function declaration equivalent of the initializer. + public var asFunctionDeclaration: FunctionDeclaration { + let dummyIdentifier = Identifier(identifierToken: Token(kind: .identifier("init"), sourceLocation: initToken.sourceLocation)) + return FunctionDeclaration(funcToken: initToken, attributes: attributes, modifiers: modifiers, identifier: dummyIdentifier, parameters: parameters, closeBracketToken: closeBracketToken, resultType: nil, body: body, closeBraceToken: closeBracketToken) + } + + public init(initToken: Token, attributes: [Attribute], modifiers: [Token], parameters: [Parameter], closeBracketToken: Token, body: [Statement], closeBraceToken: Token) { + self.initToken = initToken + self.attributes = attributes + self.modifiers = modifiers + self.parameters = parameters + self.closeBracketToken = closeBracketToken + self.body = body + self.closeBraceToken = closeBracketToken + } +} + /// A function attribute, such as `@payable`. public struct Attribute: SourceEntity { var kind: Kind diff --git a/Sources/AST/ASTDumper.swift b/Sources/AST/ASTDumper.swift index 8afcf81f..681449ca 100644 --- a/Sources/AST/ASTDumper.swift +++ b/Sources/AST/ASTDumper.swift @@ -78,8 +78,8 @@ public class ASTDumper { for callerCapability in contractBehaviorDeclaration.callerCapabilities { self.dump(callerCapability) } - for functionDeclaration in contractBehaviorDeclaration.functionDeclarations { - self.dump(functionDeclaration) + for member in contractBehaviorDeclaration.members { + self.dump(member) } self.dump(contractBehaviorDeclaration.closeBracketToken) } @@ -95,14 +95,25 @@ public class ASTDumper { } } - func dump(_ structMember: StructMember) { - switch structMember { - case .functionDeclaration(let functionDeclaration): - self.dump(functionDeclaration) - case .variableDeclaration(let variableDeclaration): - self.dump(variableDeclaration) - } + func dump(_ structMember: StructMember) { + switch structMember { + case .functionDeclaration(let functionDeclaration): + self.dump(functionDeclaration) + case .variableDeclaration(let variableDeclaration): + self.dump(variableDeclaration) + case .initializerDeclaration(let initializerDeclaration): + self.dump(initializerDeclaration) + } + } + + func dump(_ contractBehaviorMember: ContractBehaviorMember) { + switch contractBehaviorMember { + case .functionDeclaration(let functionDeclaration): + self.dump(functionDeclaration) + case .initializerDeclaration(let initializerDeclaration): + self.dump(initializerDeclaration) } + } func dump(_ variableDeclaration: VariableDeclaration) { writeNode("VariableDeclaration") { @@ -120,34 +131,45 @@ public class ASTDumper { func dump(_ functionDeclaration: FunctionDeclaration) { writeNode("FunctionDeclaration") { - for attribute in functionDeclaration.attributes { - self.dump(attribute) - } + self.dumpNodeContents(functionDeclaration) + } + } - for modifier in functionDeclaration.modifiers { - self.dump(modifier) - } - self.dump(functionDeclaration.funcToken) + func dump(_ initializerDeclaration: InitializerDeclaration) { + writeNode("InitializerDeclaration") { + self.dumpNodeContents(initializerDeclaration.asFunctionDeclaration) + } + } + + func dumpNodeContents(_ functionDeclaration: FunctionDeclaration) { + for attribute in functionDeclaration.attributes { + self.dump(attribute) + } - self.dump(functionDeclaration.identifier) + for modifier in functionDeclaration.modifiers { + self.dump(modifier) + } - for parameter in functionDeclaration.parameters { - self.dump(parameter) - } + self.dump(functionDeclaration.funcToken) - self.dump(functionDeclaration.closeBracketToken) + self.dump(functionDeclaration.identifier) - if let resultType = functionDeclaration.resultType { - self.writeNode("ResultType") { - self.dump(resultType) - } - } + for parameter in functionDeclaration.parameters { + self.dump(parameter) + } - for statement in functionDeclaration.body { - self.dump(statement) + self.dump(functionDeclaration.closeBracketToken) + + if let resultType = functionDeclaration.resultType { + self.writeNode("ResultType") { + self.dump(resultType) } } + + for statement in functionDeclaration.body { + self.dump(statement) + } } func dump(_ parameter: Parameter) { diff --git a/Sources/AST/ASTPass.swift b/Sources/AST/ASTPass.swift index 9da94a52..b5eaa35e 100644 --- a/Sources/AST/ASTPass.swift +++ b/Sources/AST/ASTPass.swift @@ -16,8 +16,10 @@ public protocol ASTPass { func process(structDeclaration: StructDeclaration, passContext: ASTPassContext) -> ASTPassResult func process(structMember: StructMember, passContext: ASTPassContext) -> ASTPassResult func process(contractBehaviorDeclaration: ContractBehaviorDeclaration, passContext: ASTPassContext) -> ASTPassResult + func process(contractBehaviorMember: ContractBehaviorMember, passContext: ASTPassContext) -> ASTPassResult func process(variableDeclaration: VariableDeclaration, passContext: ASTPassContext) -> ASTPassResult func process(functionDeclaration: FunctionDeclaration, passContext: ASTPassContext) -> ASTPassResult + func process(initializerDeclaration: InitializerDeclaration, passContext: ASTPassContext) -> ASTPassResult func process(attribute: Attribute, passContext: ASTPassContext) -> ASTPassResult func process(parameter: Parameter, passContext: ASTPassContext) -> ASTPassResult func process(typeAnnotation: TypeAnnotation, passContext: ASTPassContext) -> ASTPassResult @@ -38,9 +40,11 @@ public protocol ASTPass { func postProcess(contractDeclaration: ContractDeclaration, passContext: ASTPassContext) -> ASTPassResult func postProcess(structMember: StructMember, passContext: ASTPassContext) -> ASTPassResult func postProcess(contractBehaviorDeclaration: ContractBehaviorDeclaration, passContext: ASTPassContext) -> ASTPassResult + func postProcess(contractBehaviorMember: ContractBehaviorMember, passContext: ASTPassContext) -> ASTPassResult func postProcess(structDeclaration: StructDeclaration, passContext: ASTPassContext) -> ASTPassResult func postProcess(variableDeclaration: VariableDeclaration, passContext: ASTPassContext) -> ASTPassResult func postProcess(functionDeclaration: FunctionDeclaration, passContext: ASTPassContext) -> ASTPassResult + func postProcess(initializerDeclaration: InitializerDeclaration, passContext: ASTPassContext) -> ASTPassResult func postProcess(attribute: Attribute, passContext: ASTPassContext) -> ASTPassResult func postProcess(parameter: Parameter, passContext: ASTPassContext) -> ASTPassResult func postProcess(typeAnnotation: TypeAnnotation, passContext: ASTPassContext) -> ASTPassResult @@ -88,6 +92,10 @@ public struct AnyASTPass: ASTPass { return base.process(contractBehaviorDeclaration: contractBehaviorDeclaration, passContext: passContext) } + public func process(contractBehaviorMember: ContractBehaviorMember, passContext: ASTPassContext) -> ASTPassResult { + return base.process(contractBehaviorMember: contractBehaviorMember, passContext: passContext) + } + public func process(variableDeclaration: VariableDeclaration, passContext: ASTPassContext) -> ASTPassResult { return base.process(variableDeclaration: variableDeclaration, passContext: passContext) } @@ -96,6 +104,10 @@ public struct AnyASTPass: ASTPass { return base.process(functionDeclaration: functionDeclaration, passContext: passContext) } + public func process(initializerDeclaration: InitializerDeclaration, passContext: ASTPassContext) -> ASTPassResult { + return base.process(initializerDeclaration: initializerDeclaration, passContext: passContext) + } + public func process(attribute: Attribute, passContext: ASTPassContext) -> ASTPassResult { return base.process(attribute: attribute, passContext: passContext) } @@ -169,12 +181,16 @@ public struct AnyASTPass: ASTPass { return base.postProcess(contractBehaviorDeclaration: contractBehaviorDeclaration, passContext: passContext) } + public func postProcess(contractBehaviorMember: ContractBehaviorMember, passContext: ASTPassContext) -> ASTPassResult { + return base.postProcess(contractBehaviorMember: contractBehaviorMember, passContext: passContext) + } + public func postProcess(structDeclaration: StructDeclaration, passContext: ASTPassContext) -> ASTPassResult { - return base.process(structDeclaration: structDeclaration, passContext: passContext) + return base.postProcess(structDeclaration: structDeclaration, passContext: passContext) } public func postProcess(structMember: StructMember, passContext: ASTPassContext) -> ASTPassResult { - return base.process(structMember: structMember, passContext: passContext) + return base.postProcess(structMember: structMember, passContext: passContext) } public func postProcess(variableDeclaration: VariableDeclaration, passContext: ASTPassContext) -> ASTPassResult { @@ -185,6 +201,10 @@ public struct AnyASTPass: ASTPass { return base.postProcess(functionDeclaration: functionDeclaration, passContext: passContext) } + public func postProcess(initializerDeclaration: InitializerDeclaration, passContext: ASTPassContext) -> ASTPassResult { + return base.postProcess(initializerDeclaration: initializerDeclaration, passContext: passContext) + } + public func postProcess(attribute: Attribute, passContext: ASTPassContext) -> ASTPassResult { return base.postProcess(attribute: attribute, passContext: passContext) } diff --git a/Sources/AST/ASTVisitor.swift b/Sources/AST/ASTVisitor.swift index 63e7a203..4ee39423 100644 --- a/Sources/AST/ASTVisitor.swift +++ b/Sources/AST/ASTVisitor.swift @@ -93,9 +93,9 @@ public struct ASTVisitor { let typeScopeContext = processResult.passContext.scopeContext - processResult.element.functionDeclarations = processResult.element.functionDeclarations.map { functionDeclaration in + processResult.element.members = processResult.element.members.map { member in processResult.passContext.scopeContext = typeScopeContext - return processResult.combining(visit(functionDeclaration, passContext: processResult.passContext)) + return processResult.combining(visit(member, passContext: processResult.passContext)) } processResult.passContext.contractBehaviorDeclarationContext = nil @@ -137,12 +137,28 @@ public struct ASTVisitor { processResult.element = .functionDeclaration(processResult.combining(visit(functionDeclaration, passContext: processResult.passContext))) case .variableDeclaration(let variableDeclaration): processResult.element = .variableDeclaration(processResult.combining(visit(variableDeclaration, passContext: processResult.passContext))) + case .initializerDeclaration(let initializerDeclaration): + processResult.element = .initializerDeclaration(processResult.combining(visit(initializerDeclaration, passContext: processResult.passContext))) } let postProcessResult = pass.postProcess(structMember: processResult.element, passContext: processResult.passContext) return ASTPassResult(element: postProcessResult.element, diagnostics: processResult.diagnostics + postProcessResult.diagnostics, passContext: postProcessResult.passContext) } + func visit(_ contractBehaviorMember: ContractBehaviorMember, passContext: ASTPassContext) -> ASTPassResult { + var processResult = pass.process(contractBehaviorMember: contractBehaviorMember, passContext: passContext) + + switch processResult.element { + case .functionDeclaration(let functionDeclaration): + processResult.element = .functionDeclaration(processResult.combining(visit(functionDeclaration, passContext: processResult.passContext))) + case .initializerDeclaration(let initializerDeclaration): + processResult.element = .initializerDeclaration(processResult.combining(visit(initializerDeclaration, passContext: processResult.passContext))) + } + + let postProcessResult = pass.postProcess(contractBehaviorMember: processResult.element, passContext: processResult.passContext) + return ASTPassResult(element: postProcessResult.element, diagnostics: processResult.diagnostics + postProcessResult.diagnostics, passContext: postProcessResult.passContext) + } + func visit(_ variableDeclaration: VariableDeclaration, passContext: ASTPassContext) -> ASTPassResult { var processResult = pass.process(variableDeclaration: variableDeclaration, passContext: passContext) @@ -186,6 +202,31 @@ public struct ASTVisitor { return ASTPassResult(element: postProcessResult.element, diagnostics: processResult.diagnostics + postProcessResult.diagnostics, passContext: postProcessResult.passContext) } + func visit(_ initializerDeclaration: InitializerDeclaration, passContext: ASTPassContext) -> ASTPassResult { + var processResult = pass.process(initializerDeclaration: initializerDeclaration, passContext: passContext) + + processResult.element.attributes = processResult.element.attributes.map { attribute in + return processResult.combining(visit(attribute, passContext: processResult.passContext)) + } + + processResult.element.parameters = processResult.element.parameters.map { parameter in + return processResult.combining(visit(parameter, passContext: processResult.passContext)) + } + + let functionDeclaration = initializerDeclaration.asFunctionDeclaration + + processResult.passContext.scopeContext!.localVariables.append(contentsOf: functionDeclaration.parametersAsVariableDeclarations) + + processResult.element.body = processResult.element.body.map { statement in + return processResult.combining(visit(statement, passContext: processResult.passContext)) + } + + processResult.passContext.functionDeclarationContext = nil + + let postProcessResult = pass.postProcess(initializerDeclaration: processResult.element, passContext: processResult.passContext) + return ASTPassResult(element: postProcessResult.element, diagnostics: processResult.diagnostics + postProcessResult.diagnostics, passContext: postProcessResult.passContext) + } + func visit(_ attribute: Attribute, passContext: ASTPassContext) -> ASTPassResult { let processResult = pass.process(attribute: attribute, passContext: passContext) diff --git a/Sources/AST/ASTVisitorContext.swift b/Sources/AST/ASTVisitorContext.swift index 14c711a3..a68f070b 100644 --- a/Sources/AST/ASTVisitorContext.swift +++ b/Sources/AST/ASTVisitorContext.swift @@ -27,7 +27,7 @@ public struct StructDeclarationContext { } } -/// Contextual information used when visiting statements in a function, such as if it is mutating or note. +/// Contextual information used when visiting statements in a function, such as if it is mutating or not. public struct FunctionDeclarationContext { public var declaration: FunctionDeclaration diff --git a/Sources/AST/Token.swift b/Sources/AST/Token.swift index 782afa72..1eebb504 100644 --- a/Sources/AST/Token.swift +++ b/Sources/AST/Token.swift @@ -133,6 +133,7 @@ extension Token { case `var` case `let` case `func` + case `init` case `mutating` case `return` case `public` @@ -165,6 +166,7 @@ extension Token.Kind: CustomStringConvertible { case .var: return "var" case .let: return "let" case .func: return "func" + case .init: return "init" case .self: return "self" case .implicit: return "implicit" case .inout: return "inout" diff --git a/Sources/IRGen/IULIAContract.swift b/Sources/IRGen/IULIAContract.swift index d836147b..d541098b 100644 --- a/Sources/IRGen/IULIAContract.swift +++ b/Sources/IRGen/IULIAContract.swift @@ -24,7 +24,11 @@ struct IULIAContract { func rendered() -> String { // Generate code for each function in the contract. let functions = contractBehaviorDeclarations.flatMap { contractBehaviorDeclaration in - return contractBehaviorDeclaration.functionDeclarations.map { functionDeclaration in + return contractBehaviorDeclaration.members.compactMap { member -> IULIAFunction? in + guard case .functionDeclaration(let functionDeclaration) = member else { + return nil + // Rendering initializers is not supported yet. + } return IULIAFunction(functionDeclaration: functionDeclaration, typeIdentifier: contractDeclaration.identifier, capabilityBinding: contractBehaviorDeclaration.capabilityBinding, callerCapabilities: contractBehaviorDeclaration.callerCapabilities, environment: environment) } } diff --git a/Sources/IRGen/IULIAInterface.swift b/Sources/IRGen/IULIAInterface.swift index 64727543..6ffcacd0 100644 --- a/Sources/IRGen/IULIAInterface.swift +++ b/Sources/IRGen/IULIAInterface.swift @@ -14,8 +14,14 @@ struct IULIAInterface { func rendered() -> String { let functionSignatures = contract.contractBehaviorDeclarations.flatMap { contractBehaviorDeclaration in - return contractBehaviorDeclaration.functionDeclarations.compactMap { functionDeclaration in - return render(functionDeclaration) + return contractBehaviorDeclaration.members.compactMap { member in + switch member { + case .functionDeclaration(let functionDeclaration): + return render(functionDeclaration) + case .initializerDeclaration(_): + return "" + // Rendering initializers is not supported yet. + } } }.joined(separator: "\n") diff --git a/Sources/IRGen/IULIAPreprocessor.swift b/Sources/IRGen/IULIAPreprocessor.swift index affe8ac4..9162cf19 100644 --- a/Sources/IRGen/IULIAPreprocessor.swift +++ b/Sources/IRGen/IULIAPreprocessor.swift @@ -30,6 +30,10 @@ public struct IULIAPreprocessor: ASTPass { return ASTPassResult(element: contractBehaviorDeclaration, diagnostics: [], passContext: passContext) } + public func process(contractBehaviorMember: ContractBehaviorMember, passContext: ASTPassContext) -> ASTPassResult { + return ASTPassResult(element: contractBehaviorMember, diagnostics: [], passContext: passContext) + } + public func process(structDeclaration: StructDeclaration, passContext: ASTPassContext) -> ASTPassResult { return ASTPassResult(element: structDeclaration, diagnostics: [], passContext: passContext) } @@ -57,6 +61,10 @@ public struct IULIAPreprocessor: ASTPass { return ASTPassResult(element: functionDeclaration, diagnostics: [], passContext: passContext) } + public func process(initializerDeclaration: InitializerDeclaration, passContext: ASTPassContext) -> ASTPassResult { + return ASTPassResult(element: initializerDeclaration, diagnostics: [], passContext: passContext) + } + public func process(attribute: Attribute, passContext: ASTPassContext) -> ASTPassResult { return ASTPassResult(element: attribute, diagnostics: [], passContext: passContext) } @@ -191,6 +199,10 @@ public struct IULIAPreprocessor: ASTPass { return ASTPassResult(element: contractBehaviorDeclaration, diagnostics: [], passContext: passContext) } + public func postProcess(contractBehaviorMember: ContractBehaviorMember, passContext: ASTPassContext) -> ASTPassResult { + return ASTPassResult(element: contractBehaviorMember, diagnostics: [], passContext: passContext) + } + public func postProcess(structDeclaration: StructDeclaration, passContext: ASTPassContext) -> ASTPassResult { return ASTPassResult(element: structDeclaration, diagnostics: [], passContext: passContext) } @@ -207,6 +219,10 @@ public struct IULIAPreprocessor: ASTPass { return ASTPassResult(element: functionDeclaration, diagnostics: [], passContext: passContext) } + public func postProcess(initializerDeclaration: InitializerDeclaration, passContext: ASTPassContext) -> ASTPassResult { + return ASTPassResult(element: initializerDeclaration, diagnostics: [], passContext: passContext) + } + public func postProcess(attribute: Attribute, passContext: ASTPassContext) -> ASTPassResult { return ASTPassResult(element: attribute, diagnostics: [], passContext: passContext) } diff --git a/Sources/Optimizer/Optimizer.swift b/Sources/Optimizer/Optimizer.swift index 9169b8c7..ae7516ee 100644 --- a/Sources/Optimizer/Optimizer.swift +++ b/Sources/Optimizer/Optimizer.swift @@ -27,6 +27,10 @@ public struct Optimizer: ASTPass { return ASTPassResult(element: contractBehaviorDeclaration, diagnostics: [], passContext: passContext) } + public func process(contractBehaviorMember: ContractBehaviorMember, passContext: ASTPassContext) -> ASTPassResult { + return ASTPassResult(element: contractBehaviorMember, diagnostics: [], passContext: passContext) + } + public func process(structDeclaration: StructDeclaration, passContext: ASTPassContext) -> ASTPassResult { return ASTPassResult(element: structDeclaration, diagnostics: [], passContext: passContext) } @@ -43,6 +47,10 @@ public struct Optimizer: ASTPass { return ASTPassResult(element: functionDeclaration, diagnostics: [], passContext: passContext) } + public func process(initializerDeclaration: InitializerDeclaration, passContext: ASTPassContext) -> ASTPassResult { + return ASTPassResult(element: initializerDeclaration, diagnostics: [], passContext: passContext) + } + public func process(attribute: Attribute, passContext: ASTPassContext) -> ASTPassResult { return ASTPassResult(element: attribute, diagnostics: [], passContext: passContext) } @@ -115,6 +123,10 @@ public struct Optimizer: ASTPass { return ASTPassResult(element: contractBehaviorDeclaration, diagnostics: [], passContext: passContext) } + public func postProcess(contractBehaviorMember: ContractBehaviorMember, passContext: ASTPassContext) -> ASTPassResult { + return ASTPassResult(element: contractBehaviorMember, diagnostics: [], passContext: passContext) + } + public func postProcess(structDeclaration: StructDeclaration, passContext: ASTPassContext) -> ASTPassResult { return ASTPassResult(element: structDeclaration, diagnostics: [], passContext: passContext) } @@ -131,6 +143,10 @@ public struct Optimizer: ASTPass { return ASTPassResult(element: functionDeclaration, diagnostics: [], passContext: passContext) } + public func postProcess(initializerDeclaration: InitializerDeclaration, passContext: ASTPassContext) -> ASTPassResult { + return ASTPassResult(element: initializerDeclaration, diagnostics: [], passContext: passContext) + } + public func postProcess(attribute: Attribute, passContext: ASTPassContext) -> ASTPassResult { return ASTPassResult(element: attribute, diagnostics: [], passContext: passContext) } diff --git a/Sources/Parser/Parser.swift b/Sources/Parser/Parser.swift index 6cf77e6a..3489a423 100644 --- a/Sources/Parser/Parser.swift +++ b/Sources/Parser/Parser.swift @@ -339,14 +339,16 @@ extension Parser { let (callerCapabilities, closeBracketToken) = try parseCallerCapabilityGroup() try consume(.punctuation(.openBrace)) - let functionDeclarations = try parseContractFunctionDeclarations(contractIdentifier: contractIdentifier) + let members = try parseContractBehaviorMembers() + try consume(.punctuation(.closeBrace)) - for functionDeclaration in functionDeclarations { + for case .functionDeclaration(let functionDeclaration) in members { + // Record all the function declarations. environment.addFunction(functionDeclaration, enclosingType: contractIdentifier.name, callerCapabilities: callerCapabilities) } - - return ContractBehaviorDeclaration(contractIdentifier: contractIdentifier, capabilityBinding: capabilityBinding, callerCapabilities: callerCapabilities, closeBracketToken: closeBracketToken, functionDeclarations: functionDeclarations) + + return ContractBehaviorDeclaration(contractIdentifier: contractIdentifier, capabilityBinding: capabilityBinding, callerCapabilities: callerCapabilities, closeBracketToken: closeBracketToken, members: members) } func parseCapabilityBinding() throws -> Identifier { @@ -372,18 +374,24 @@ extension Parser { return callerCapabilities } - - func parseContractFunctionDeclarations(contractIdentifier: Identifier) throws -> [FunctionDeclaration] { - var functionDeclarations = [FunctionDeclaration]() - - while let functionDeclaration = attempt(try parseFunctionDeclaration(typeIdentifier: contractIdentifier)) { - functionDeclarations.append(functionDeclaration) + + func parseContractBehaviorMembers() throws -> [ContractBehaviorMember] { + var members = [ContractBehaviorMember]() + + while true { + if let functionDeclaration = attempt(task: parseFunctionDeclaration) { + members.append(.functionDeclaration(functionDeclaration)) + } else if let initializerDeclaration = attempt(task: parseInitializerDeclaration) { + members.append(.initializerDeclaration(initializerDeclaration)) + } else { + break + } } - - return functionDeclarations + + return members } - func parseFunctionDeclaration(typeIdentifier: Identifier) throws -> FunctionDeclaration { + func parseFunctionDeclaration() throws -> FunctionDeclaration { let (attributes, modifiers, funcToken) = try parseFunctionHead() let identifier = try parseIdentifier() let (parameters, closeBracketToken) = try parseParameters() @@ -392,8 +400,16 @@ extension Parser { return FunctionDeclaration(funcToken: funcToken, attributes: attributes, modifiers: modifiers, identifier: identifier, parameters: parameters, closeBracketToken: closeBracketToken, resultType: resultType, body: body, closeBraceToken: closeBraceToken) } - - func parseFunctionHead() throws -> (attributes: [Attribute], modifiers: [Token], funcToken: Token) { + + func parseInitializerDeclaration() throws -> InitializerDeclaration { + let (attributes, modifiers, initToken) = try parseInitializerHead() + let (parameters, closeBracketToken) = try parseParameters() + let (body, closeBraceToken) = try parseCodeBlock() + + return InitializerDeclaration(initToken: initToken, attributes: attributes, modifiers: modifiers, parameters: parameters, closeBracketToken: closeBracketToken, body: body, closeBraceToken: closeBraceToken) + } + + func parseAttributesAndModifiers() throws -> (attributes: [Attribute], modifiers: [Token]) { var attributes = [Attribute]() var modifiers = [Token]() @@ -412,10 +428,23 @@ extension Parser { break } } + + return (attributes, modifiers) + } + + func parseFunctionHead() throws -> (attributes: [Attribute], modifiers: [Token], funcToken: Token) { + let (attributes, modifiers) = try parseAttributesAndModifiers() let funcToken = try consume(.func) return (attributes, modifiers, funcToken) } + + func parseInitializerHead() throws -> (attributes: [Attribute], modifiers: [Token], initToken: Token) { + let (attributes, modifiers) = try parseAttributesAndModifiers() + + let initToken = try consume(.init) + return (attributes, modifiers, initToken) + } func parseParameters() throws -> ([Parameter], closeBracketToken: Token) { try consume(.punctuation(.openBracket)) @@ -632,9 +661,11 @@ extension Parser { while true { if let variableDeclaration = attempt(try parseVariableDeclaration(asTypeProperty: true)) { members.append(.variableDeclaration(variableDeclaration)) - } else if let functionDeclaration = attempt(try parseFunctionDeclaration(typeIdentifier: structIdentifier)) { + } else if let functionDeclaration = attempt(task: parseFunctionDeclaration) { members.append(.functionDeclaration(functionDeclaration)) environment.addFunction(functionDeclaration, enclosingType: structIdentifier.name) + } else if let initializerDeclaration = attempt(task: parseInitializerDeclaration) { + members.append(.initializerDeclaration(initializerDeclaration)) } else { break } diff --git a/Sources/Parser/Tokenizer.swift b/Sources/Parser/Tokenizer.swift index c9421aa3..62f12c1e 100644 --- a/Sources/Parser/Tokenizer.swift +++ b/Sources/Parser/Tokenizer.swift @@ -68,6 +68,7 @@ public struct Tokenizer { "var": .var, "let": .let, "func": .func, + "init": .init, "mutating": .mutating, "return": .return, "public": .public, diff --git a/Sources/SemanticAnalyzer/SemanticAnalyzer.swift b/Sources/SemanticAnalyzer/SemanticAnalyzer.swift index d6660116..d4f4dc03 100644 --- a/Sources/SemanticAnalyzer/SemanticAnalyzer.swift +++ b/Sources/SemanticAnalyzer/SemanticAnalyzer.swift @@ -42,6 +42,10 @@ public struct SemanticAnalyzer: ASTPass { return ASTPassResult(element: contractBehaviorDeclaration, diagnostics: diagnostics, passContext: passContext) } + public func process(contractBehaviorMember: ContractBehaviorMember, passContext: ASTPassContext) -> ASTPassResult { + return ASTPassResult(element: contractBehaviorMember, diagnostics: [], passContext: passContext) + } + public func process(structDeclaration: StructDeclaration, passContext: ASTPassContext) -> ASTPassResult { return ASTPassResult(element: structDeclaration, diagnostics: [], passContext: passContext) } @@ -122,6 +126,10 @@ public struct SemanticAnalyzer: ASTPass { return ASTPassResult(element: functionDeclaration, diagnostics: diagnostics, passContext: passContext) } + public func process(initializerDeclaration: InitializerDeclaration, passContext: ASTPassContext) -> ASTPassResult { + return ASTPassResult(element: initializerDeclaration, diagnostics: [], passContext: passContext) + } + public func process(attribute: Attribute, passContext: ASTPassContext) -> ASTPassResult { return ASTPassResult(element: attribute, diagnostics: [], passContext: passContext) } @@ -289,6 +297,10 @@ public struct SemanticAnalyzer: ASTPass { return ASTPassResult(element: contractBehaviorDeclaration, diagnostics: [], passContext: passContext) } + public func postProcess(contractBehaviorMember: ContractBehaviorMember, passContext: ASTPassContext) -> ASTPassResult { + return ASTPassResult(element: contractBehaviorMember, diagnostics: [], passContext: passContext) + } + public func postProcess(structDeclaration: StructDeclaration, passContext: ASTPassContext) -> ASTPassResult { return ASTPassResult(element: structDeclaration, diagnostics: [], passContext: passContext) } @@ -317,6 +329,10 @@ public struct SemanticAnalyzer: ASTPass { return ASTPassResult(element: functionDeclaration, diagnostics: diagnostics, passContext: passContext) } + public func postProcess(initializerDeclaration: InitializerDeclaration, passContext: ASTPassContext) -> ASTPassResult { + return ASTPassResult(element: initializerDeclaration, diagnostics: [], passContext: passContext) + } + public func postProcess(attribute: Attribute, passContext: ASTPassContext) -> ASTPassResult { return ASTPassResult(element: attribute, diagnostics: [], passContext: passContext) } diff --git a/Sources/SemanticAnalyzer/TypeChecker.swift b/Sources/SemanticAnalyzer/TypeChecker.swift index fd075734..7245d4e5 100644 --- a/Sources/SemanticAnalyzer/TypeChecker.swift +++ b/Sources/SemanticAnalyzer/TypeChecker.swift @@ -27,6 +27,10 @@ public struct TypeChecker: ASTPass { return ASTPassResult(element: contractBehaviorDeclaration, diagnostics: [], passContext: passContext) } + public func process(contractBehaviorMember: ContractBehaviorMember, passContext: ASTPassContext) -> ASTPassResult { + return ASTPassResult(element: contractBehaviorMember, diagnostics: [], passContext: passContext) + } + public func process(structDeclaration: StructDeclaration, passContext: ASTPassContext) -> ASTPassResult { return ASTPassResult(element: structDeclaration, diagnostics: [], passContext: passContext) } @@ -65,6 +69,10 @@ public struct TypeChecker: ASTPass { public func process(functionDeclaration: FunctionDeclaration, passContext: ASTPassContext) -> ASTPassResult { return ASTPassResult(element: functionDeclaration, diagnostics: [], passContext: passContext) } + + public func process(initializerDeclaration: InitializerDeclaration, passContext: ASTPassContext) -> ASTPassResult { + return ASTPassResult(element: initializerDeclaration, diagnostics: [], passContext: passContext) + } public func process(attribute: Attribute, passContext: ASTPassContext) -> ASTPassResult { return ASTPassResult(element: attribute, diagnostics: [], passContext: passContext) @@ -188,6 +196,10 @@ public struct TypeChecker: ASTPass { return ASTPassResult(element: contractBehaviorDeclaration, diagnostics: [], passContext: passContext) } + public func postProcess(contractBehaviorMember: ContractBehaviorMember, passContext: ASTPassContext) -> ASTPassResult { + return ASTPassResult(element: contractBehaviorMember, diagnostics: [], passContext: passContext) + } + public func postProcess(structDeclaration: StructDeclaration, passContext: ASTPassContext) -> ASTPassResult { return ASTPassResult(element: structDeclaration, diagnostics: [], passContext: passContext) } @@ -203,6 +215,10 @@ public struct TypeChecker: ASTPass { public func postProcess(functionDeclaration: FunctionDeclaration, passContext: ASTPassContext) -> ASTPassResult { return ASTPassResult(element: functionDeclaration, diagnostics: [], passContext: passContext) } + + public func postProcess(initializerDeclaration: InitializerDeclaration, passContext: ASTPassContext) -> ASTPassResult { + return ASTPassResult(element: initializerDeclaration, diagnostics: [], passContext: passContext) + } public func postProcess(attribute: Attribute, passContext: ASTPassContext) -> ASTPassResult { return ASTPassResult(element: attribute, diagnostics: [], passContext: passContext) diff --git a/Tests/ParserTests/structs.flint b/Tests/ParserTests/structs.flint index 23fc05ba..34b6757b 100644 --- a/Tests/ParserTests/structs.flint +++ b/Tests/ParserTests/structs.flint @@ -17,6 +17,14 @@ struct MyStruct { // CHECK-AST: built-in type Int var a: Int +// CHECK-AST: InitializerDeclaration +// CHECK-AST: Parameter +// CHECK-AST: identifier "a" +// CHECK-AST: built-in type Int + init(a: Int) { + self.a = a + } + // CHECK-AST: FunctionDeclaration // CHECK-AST: identifier "foo" func foo() {} diff --git a/Tests/ParserTests/types.flint b/Tests/ParserTests/types.flint index d954794a..63da9d3a 100644 --- a/Tests/ParserTests/types.flint +++ b/Tests/ParserTests/types.flint @@ -37,3 +37,14 @@ contract Foo { // CHECK-AST: literal 2 var assignedValue: Int = 2 } + +Foo :: (any) { + +// CHECK-AST: InitializerDeclaration +// CHECK-AST: Parameter +// CHECK-AST: identifier "assignedValue" +// CHECK-AST: built-in type Int + init(assignedValue: Int) { + self.assignedValue = assignedValue + } +} diff --git a/docs/grammar.txt b/docs/grammar.txt index 1ef6599f..db12c179 100644 --- a/docs/grammar.txt +++ b/docs/grammar.txt @@ -27,11 +27,13 @@ generic-parameter-list -> type | type [,] generic-argument-list struct-declaration -> [struct] identifier [{] (struct-members) [}] struct-members -> struct-member (struct-members) -struct-member -> variable-declaration | function-declaration +struct-member -> variable-declaration | function-declaration | initializer-declaration // Contract behavior declaration -contract-behavior-declaration -> identifier [::] (caller-capability-binding) caller-capability-group (function-declarations) +contract-behavior-declaration -> identifier [::] (caller-capability-binding) caller-capability-group (contract-behavior-members) +contract-behavior-members -> contract-behavior-member (contract-behavior-members) +contract-behavior-member -> function-declaration | initializer-declaration // Caller capability group @@ -44,12 +46,14 @@ caller-capability-binding -> identifier [<-] identifier -> [a-zA-Z] . [a-zA-Z0-9]* -// Function declarations +// Function and initializer declarations -function-declarations -> function-declaration (function-declarations) +initializer-declaration -> initializer-head parameter-clause code-block function-declaration -> function-head identifier parameter-clause (function-result) code-block +initializer-head -> (declaration-attributes) (declaration-modifiers) [init] function-head -> (declaration-attributes) (declaration-modifiers) [func] + declaration-modifier -> [public] | [mutating] declaration-modifiers -> declaration-modifier (declaration-modifiers) From 68b5e5c35eaa6d63fc2389ae57f05942492c96f5 Mon Sep 17 00:00:00 2001 From: Franklin Schrans Date: Sat, 28 Apr 2018 13:47:34 +0100 Subject: [PATCH 2/7] Render contract initializers --- Sources/AST/AST.swift | 14 +++ Sources/IRGen/Function/IULIAFunction.swift | 60 +++++++++---- Sources/IRGen/Function/IULIAInitializer.swift | 88 +++++++++++++++++++ Sources/IRGen/IULIAContract.swift | 78 ++++++++++------ .../SemanticAnalyzer/SemanticAnalyzer.swift | 29 +++++- Sources/SemanticAnalyzer/SemanticError.swift | 5 ++ Tests/BehaviorTests/tests/bank/bank.flint | 4 + .../tests/bank/test/migrations/1.js | 2 +- Tests/BehaviorTests/tests/inits/inits.flint | 25 ++++++ .../BehaviorTests/tests/inits/test/config.js | 1 + .../tests/inits/test/migrations/1.js | 7 ++ .../tests/inits/test/test/.placeholder | 1 + .../tests/inits/test/test/test.js | 23 +++++ .../tests/inits/test/truffle-config.js | 4 + .../BehaviorTests/tests/inits/test/truffle.js | 4 + .../BehaviorTests/tests/structs/structs.flint | 2 + Tests/SemanticTests/multiple_inits.flint | 18 ++++ 17 files changed, 323 insertions(+), 42 deletions(-) create mode 100644 Sources/IRGen/Function/IULIAInitializer.swift create mode 100644 Tests/BehaviorTests/tests/inits/inits.flint create mode 100644 Tests/BehaviorTests/tests/inits/test/config.js create mode 100644 Tests/BehaviorTests/tests/inits/test/migrations/1.js create mode 100644 Tests/BehaviorTests/tests/inits/test/test/.placeholder create mode 100644 Tests/BehaviorTests/tests/inits/test/test/test.js create mode 100644 Tests/BehaviorTests/tests/inits/test/truffle-config.js create mode 100644 Tests/BehaviorTests/tests/inits/test/truffle.js create mode 100644 Tests/SemanticTests/multiple_inits.flint diff --git a/Sources/AST/AST.swift b/Sources/AST/AST.swift index 0fb4d613..751aca0d 100644 --- a/Sources/AST/AST.swift +++ b/Sources/AST/AST.swift @@ -235,12 +235,26 @@ public struct InitializerDeclaration: SourceEntity { return initToken.sourceLocation } + /// The non-implicit parameters of the initializer. + public var explicitParameters: [Parameter] { + return asFunctionDeclaration.explicitParameters + } + /// A function declaration equivalent of the initializer. public var asFunctionDeclaration: FunctionDeclaration { let dummyIdentifier = Identifier(identifierToken: Token(kind: .identifier("init"), sourceLocation: initToken.sourceLocation)) return FunctionDeclaration(funcToken: initToken, attributes: attributes, modifiers: modifiers, identifier: dummyIdentifier, parameters: parameters, closeBracketToken: closeBracketToken, resultType: nil, body: body, closeBraceToken: closeBracketToken) } + /// The parameters of the initializer, as variable declaration values. + public var parametersAsVariableDeclarations: [VariableDeclaration] { + return asFunctionDeclaration.parametersAsVariableDeclarations + } + + public var isPublic: Bool { + return asFunctionDeclaration.isPublic + } + public init(initToken: Token, attributes: [Attribute], modifiers: [Token], parameters: [Parameter], closeBracketToken: Token, body: [Statement], closeBraceToken: Token) { self.initToken = initToken self.attributes = attributes diff --git a/Sources/IRGen/Function/IULIAFunction.swift b/Sources/IRGen/Function/IULIAFunction.swift index 6f09f411..fae70979 100644 --- a/Sources/IRGen/Function/IULIAFunction.swift +++ b/Sources/IRGen/Function/IULIAFunction.swift @@ -6,7 +6,6 @@ // import AST -import Foundation import CryptoSwift /// Generates code for a function. @@ -71,6 +70,49 @@ struct IULIAFunction { let parametersString = parameterNames.joined(separator: ", ") let signature = "\(name)(\(parametersString)) \(doesReturn ? "-> \(IULIAFunction.returnVariableName)" : "")" + let body = IULIAFunctionBody(functionDeclaration: functionDeclaration, typeIdentifier: typeIdentifier, capabilityBinding: capabilityBinding, callerCapabilities: callerCapabilities, environment: environment, isContractFunction: isContractFunction).rendered() + + return """ + function \(signature) { + \(body.indented(by: 2)) + } + """ + } + + /// The string representation of this function's signature, used for generating a IULIA interface. + func mangledSignature() -> String { + let name = functionDeclaration.identifier.name + let parametersString = parameterCanonicalTypes.map({ $0.rawValue }).joined(separator: ",") + + return "\(name)(\(parametersString))" + } +} + +struct IULIAFunctionBody { + var functionDeclaration: FunctionDeclaration + var typeIdentifier: Identifier + + var capabilityBinding: Identifier? + var callerCapabilities: [CallerCapability] + + var environment: Environment + + var isContractFunction = false + + /// The function's parameters and caller capability binding, as variable declarations in a `ScopeContext`. + var scopeContext: ScopeContext { + var localVariables = functionDeclaration.parametersAsVariableDeclarations + if let capabilityBinding = capabilityBinding { + localVariables.append(VariableDeclaration(declarationToken: nil, identifier: capabilityBinding, type: Type(inferredType: .builtInType(.address), identifier: capabilityBinding))) + } + return ScopeContext(localVariables: localVariables) + } + + var functionContext: FunctionContext { + return FunctionContext(environment: environment, scopeContext: scopeContext, enclosingTypeName: typeIdentifier.name, isInContractFunction: isContractFunction) + } + + func rendered() -> String { // Dynamically check the caller has appropriate caller capabilities. let callerCapabilityChecks = IULIACallerCapabilityChecks(callerCapabilities: callerCapabilities).rendered(functionContext: functionContext) let body = renderBody(functionDeclaration.body, functionContext: functionContext) @@ -91,11 +133,7 @@ struct IULIAFunction { payableValueDeclaration = "" } - return """ - function \(signature) { - \(callerCapabilityChecks.indented(by: 2))\(payableValueDeclaration.indented(by: 2))\(capabilityBindingDeclaration.indented(by: 2))\(body.indented(by: 2)) - } - """ + return "\(callerCapabilityChecks)\(payableValueDeclaration)\(capabilityBindingDeclaration)\(body)" } func renderBody(_ statements: S, functionContext: FunctionContext) -> String where S.Element == AST.Statement, S.Index == Int { @@ -109,7 +147,7 @@ struct IULIAFunction { let defaultCode = """ default { - \(restCode.indented(by: 2)) + \(restCode.indented(by: 2)) } """ return firstCode + (restCode.isEmpty ? "" : defaultCode) @@ -117,14 +155,6 @@ struct IULIAFunction { return firstCode + (restCode.isEmpty ? "" : "\n" + restCode) } } - - /// The string representation of this function's signature, used for generating a IULIA interface. - func mangledSignature() -> String { - let name = functionDeclaration.identifier.name - let parametersString = parameterCanonicalTypes.map({ $0.rawValue }).joined(separator: ",") - - return "\(name)(\(parametersString))" - } } /// Checks whether the caller of a function has appropriate caller capabilities. diff --git a/Sources/IRGen/Function/IULIAInitializer.swift b/Sources/IRGen/Function/IULIAInitializer.swift new file mode 100644 index 00000000..dc6c2ae9 --- /dev/null +++ b/Sources/IRGen/Function/IULIAInitializer.swift @@ -0,0 +1,88 @@ +// +// IULIAInitializer.swift +// IRGen +// +// Created by Franklin Schrans on 4/27/18. +// + +import AST + +/// Generates code for an initializer. +struct IULIAInitializer { + var initializerDeclaration: InitializerDeclaration + var typeIdentifier: Identifier + + /// The properties defined in the enclosing type. The default values of each property will be set in the initializer. + var propertiesInEnclosingType: [VariableDeclaration] + + var capabilityBinding: Identifier? + var callerCapabilities: [CallerCapability] + + var environment: Environment + + var isContractFunction = false + + var functionContext: FunctionContext { + return FunctionContext(environment: environment, scopeContext: scopeContext, enclosingTypeName: typeIdentifier.name, isInContractFunction: isContractFunction) + } + + var parameterNames: [String] { + return initializerDeclaration.explicitParameters.map { parameter in + return IULIAIdentifier(identifier: parameter.identifier).rendered(functionContext: functionContext) + } + } + + /// The function's parameters and caller capability binding, as variable declarations in a `ScopeContext`. + var scopeContext: ScopeContext { + var localVariables = initializerDeclaration.parametersAsVariableDeclarations + if let capabilityBinding = capabilityBinding { + localVariables.append(VariableDeclaration(declarationToken: nil, identifier: capabilityBinding, type: Type(inferredType: .builtInType(.address), identifier: capabilityBinding))) + } + return ScopeContext(localVariables: localVariables) + } + + func rendered() -> String { + let parameterSizes = initializerDeclaration.explicitParameters.map { environment.size(of: $0.type.rawType) } + let offsetsAndSizes = zip(parameterSizes.reversed().reduce((0, [Int]())) { (acc, element) in + let (size, sizes) = acc + let nextSize = size + element * 32 + return (nextSize, sizes + [nextSize]) + }.1.reversed(), parameterSizes) + + let parameterBindings = zip(parameterNames, offsetsAndSizes).map { arg -> String in + let (parameter, (offset, size)) = arg + return """ + codecopy(0x0, sub(codesize, \(offset)), \(size * 32)) + let \(parameter) := mload(0) + """ + }.joined(separator: "\n") + + let defaultValuesAssignments = renderDefaultValuesAssignments() + + let body = IULIAFunctionBody(functionDeclaration: initializerDeclaration.asFunctionDeclaration, typeIdentifier: typeIdentifier, capabilityBinding: capabilityBinding, callerCapabilities: callerCapabilities, environment: environment, isContractFunction: isContractFunction).rendered() + + return """ + \(parameterBindings) + \(defaultValuesAssignments) + \(body) + """ + } + + func renderDefaultValuesAssignments() -> String { + let defaultValueAssignments = propertiesInEnclosingType.compactMap { declaration -> String? in + guard let assignedExpression = declaration.assignedExpression else { return nil } +// let offset = environment.propertyOffset(for: declaration.identifier.name, enclosingType: typeIdentifier.name)! + guard case .literal(let literalToken) = assignedExpression else { + fatalError("Non-literal default values are not supported yet") + } + + var identifier = declaration.identifier + identifier.enclosingType = typeIdentifier.name + + return IULIAAssignment(lhs: .identifier(identifier), rhs: .literal(literalToken)).rendered(functionContext: functionContext) +// return "sstore(\(), \(IULIALiteralToken(literalToken: literalToken).rendered()));" + } + + return defaultValueAssignments.joined(separator: "\n") + } +} diff --git a/Sources/IRGen/IULIAContract.swift b/Sources/IRGen/IULIAContract.swift index d541098b..9827f87f 100644 --- a/Sources/IRGen/IULIAContract.swift +++ b/Sources/IRGen/IULIAContract.swift @@ -46,8 +46,10 @@ struct IULIAContract { } } + // TODO: Generate code for initializers too. + let structsFunctionsCode = structFunctions.map({ $0.rendered() }).joined(separator: "\n\n").indented(by: 6) - let initializerBody = renderInitializer() + let initializerBody = renderInitializers() var index = 0 var propertyDeclarations = [String]() @@ -68,7 +70,7 @@ struct IULIAContract { // Main contract body. return """ - pragma solidity ^0.4.19; + pragma solidity ^0.4.21; contract \(contractDeclaration.identifier.name) { @@ -97,32 +99,58 @@ struct IULIAContract { """ } - func renderInitializer() -> String { - // Generate an initializer which takes in the 256-bit values in storage. - let initializerParameters = contractDeclaration.variableDeclarations.filter { $0.type.rawType.isBasicType && !$0.type.rawType.isEventType && $0.assignedExpression == nil } - let initializerParameterList = initializerParameters.map { "\(CanonicalType(from: $0.type.rawType)!.rawValue) \($0.identifier.name)" }.joined(separator: ", ") - var initializerBody = initializerParameters.map { parameter in - let offset = environment.propertyOffset(for: parameter.identifier.name, enclosingType: contractDeclaration.identifier.name)! - return "_flintStorage\(offset) = \(parameter.identifier.name);" - }.joined(separator: "\n") - - let defaultValueAssignments = contractDeclaration.variableDeclarations.compactMap { declaration -> String? in - guard let assignedExpression = declaration.assignedExpression else { return nil } - let offset = environment.propertyOffset(for: declaration.identifier.name, enclosingType: contractDeclaration.identifier.name)! - guard case .literal(let literalToken) = assignedExpression else { - fatalError("Non-literal default values are not supported yet") - } - return "_flintStorage\(offset) = \(IULIALiteralToken(literalToken: literalToken).rendered());" - } + func renderInitializers() -> String { + return contractBehaviorDeclarations.flatMap { contractBehaviorDeclaration in + return contractBehaviorDeclaration.members.compactMap { member -> String? in + guard case .initializerDeclaration(let initializerDeclaration) = member else { + return nil + } - initializerBody += "\n" + defaultValueAssignments.joined(separator: "\n") - return """ - function \(contractDeclaration.identifier.name)(\(initializerParameterList)) public { - \(initializerBody.indented(by: 2)) - } - """ + let initializer = IULIAInitializer(initializerDeclaration: initializerDeclaration, typeIdentifier: contractDeclaration.identifier, propertiesInEnclosingType: contractDeclaration.variableDeclarations, capabilityBinding: contractBehaviorDeclaration.capabilityBinding, callerCapabilities: contractBehaviorDeclaration.callerCapabilities, environment: environment, isContractFunction: true).rendered() + + let parameters = initializerDeclaration.parameters.map { parameter in + let parameterName = Mangler.mangleName(parameter.identifier.name) + return "\(CanonicalType(from: parameter.type.rawType)!.rawValue) \(parameterName)" + }.joined(separator: ", ") + + // TODO: Assign default values set at property declarations. + + return """ + constructor(\(parameters)) public { + assembly { + \(initializer.indented(by: 4)) + } + } + """ + } + }.joined(separator: "\n") } +// // Generate an initializer which takes in the 256-bit values in storage. +// let initializerParameters = contractDeclaration.variableDeclarations.filter { $0.type.rawType.isBasicType && !$0.type.rawType.isEventType && $0.assignedExpression == nil } +// let initializerParameterList = initializerParameters.map { "\(CanonicalType(from: $0.type.rawType)!.rawValue) \($0.identifier.name)" }.joined(separator: ", ") +// var initializerBody = initializerParameters.map { parameter in +// let offset = environment.propertyOffset(for: parameter.identifier.name, enclosingType: contractDeclaration.identifier.name)! +// return "_flintStorage\(offset) = \(parameter.identifier.name);" +// }.joined(separator: "\n") +// +// let defaultValueAssignments = contractDeclaration.variableDeclarations.compactMap { declaration -> String? in +// guard let assignedExpression = declaration.assignedExpression else { return nil } +// let offset = environment.propertyOffset(for: declaration.identifier.name, enclosingType: contractDeclaration.identifier.name)! +// guard case .literal(let literalToken) = assignedExpression else { +// fatalError("Non-literal default values are not supported yet") +// } +// return "_flintStorage\(offset) = \(IULIALiteralToken(literalToken: literalToken).rendered());" +// } +// +// initializerBody += "\n" + defaultValueAssignments.joined(separator: "\n") +// return """ +// function \(contractDeclaration.identifier.name)(\(initializerParameterList)) public { +// \(initializerBody.indented(by: 2)) +// } +// """ +// } + /// The list of canonical types for the given type. Fixed-size arrays of size `n` will result in a list of `n` /// canonical types. public func storageCanonicalTypes(for type: Type.RawType) -> [String] { diff --git a/Sources/SemanticAnalyzer/SemanticAnalyzer.swift b/Sources/SemanticAnalyzer/SemanticAnalyzer.swift index d4f4dc03..091fcfa6 100644 --- a/Sources/SemanticAnalyzer/SemanticAnalyzer.swift +++ b/Sources/SemanticAnalyzer/SemanticAnalyzer.swift @@ -127,7 +127,17 @@ public struct SemanticAnalyzer: ASTPass { } public func process(initializerDeclaration: InitializerDeclaration, passContext: ASTPassContext) -> ASTPassResult { - return ASTPassResult(element: initializerDeclaration, diagnostics: [], passContext: passContext) + var diagnostics = [Diagnostic]() + var passContext = passContext + + if initializerDeclaration.isPublic { + if let publicInitializerSourceLocation = passContext.publicInitializerSourceLocation { + diagnostics.append(.multiplePublicInitializersDefined(initializerDeclaration, originalInitializerLocation: publicInitializerSourceLocation)) + } else { + passContext.publicInitializerSourceLocation = initializerDeclaration.sourceLocation + } + } + return ASTPassResult(element: initializerDeclaration, diagnostics: diagnostics, passContext: passContext) } public func process(attribute: Attribute, passContext: ASTPassContext) -> ASTPassResult { @@ -294,6 +304,9 @@ public struct SemanticAnalyzer: ASTPass { } public func postProcess(contractBehaviorDeclaration: ContractBehaviorDeclaration, passContext: ASTPassContext) -> ASTPassResult { + let passContext = passContext.withUpdates { + $0.publicInitializerSourceLocation = nil + } return ASTPassResult(element: contractBehaviorDeclaration, diagnostics: [], passContext: passContext) } @@ -302,6 +315,9 @@ public struct SemanticAnalyzer: ASTPass { } public func postProcess(structDeclaration: StructDeclaration, passContext: ASTPassContext) -> ASTPassResult { + let passContext = passContext.withUpdates { + $0.publicInitializerSourceLocation = nil + } return ASTPassResult(element: structDeclaration, diagnostics: [], passContext: passContext) } @@ -439,12 +455,23 @@ public struct SemanticAnalyzer: ASTPass { } extension ASTPassContext { + /// The list of mutating expressions in a function. var mutatingExpressions: [Expression]? { get { return self[MutatingExpressionContextEntry.self] } set { self[MutatingExpressionContextEntry.self] = newValue } } + + /// The source location of the public initializer in a type, if one has been declared. + public var publicInitializerSourceLocation: SourceLocation? { + get { return self[PublicInitializerSourceLocation.self] } + set { self[PublicInitializerSourceLocation.self] = newValue } + } } struct MutatingExpressionContextEntry: PassContextEntry { typealias Value = [Expression] } + +struct PublicInitializerSourceLocation: PassContextEntry { + typealias Value = SourceLocation +} diff --git a/Sources/SemanticAnalyzer/SemanticError.swift b/Sources/SemanticAnalyzer/SemanticError.swift index ddca2a06..e78c7ef1 100644 --- a/Sources/SemanticAnalyzer/SemanticError.swift +++ b/Sources/SemanticAnalyzer/SemanticError.swift @@ -60,6 +60,11 @@ extension Diagnostic { return Diagnostic(severity: .error, sourceLocation: variableDeclaration.sourceLocation, message: "'let' constant 'e' needs to be assigned a value") } + static func multiplePublicInitializersDefined(_ invalidAdditionalInitializer: InitializerDeclaration, originalInitializerLocation: SourceLocation) -> Diagnostic { + let note = Diagnostic(severity: .note, sourceLocation: originalInitializerLocation, message: "A public initializer is already declared here") + return Diagnostic(severity: .error, sourceLocation: invalidAdditionalInitializer.sourceLocation, message: "A public initializer has already been defined", notes: [note]) + } + static func renderCapabilityGroup(_ capabilities: [CallerCapability]) -> String { return "(\(capabilities.map({ $0.name }).joined(separator: ", ")))" } diff --git a/Tests/BehaviorTests/tests/bank/bank.flint b/Tests/BehaviorTests/tests/bank/bank.flint index afd08697..5c383da2 100644 --- a/Tests/BehaviorTests/tests/bank/bank.flint +++ b/Tests/BehaviorTests/tests/bank/bank.flint @@ -8,6 +8,10 @@ contract Bank { } Bank :: account <- (any) { + init(manager: Address) { + self.manager = manager + } + public mutating func register() { accounts[lastIndex] = account lastIndex += 1 diff --git a/Tests/BehaviorTests/tests/bank/test/migrations/1.js b/Tests/BehaviorTests/tests/bank/test/migrations/1.js index 4e03200d..13f4a536 100644 --- a/Tests/BehaviorTests/tests/bank/test/migrations/1.js +++ b/Tests/BehaviorTests/tests/bank/test/migrations/1.js @@ -3,5 +3,5 @@ var config = require("../config.js") var Contract = artifacts.require("./" + config.contractName + ".sol"); module.exports = function(deployer, network, accounts) { - deployer.deploy(Contract, accounts[9], 0, 0); + deployer.deploy(Contract, accounts[9]); }; diff --git a/Tests/BehaviorTests/tests/inits/inits.flint b/Tests/BehaviorTests/tests/inits/inits.flint new file mode 100644 index 00000000..b38583aa --- /dev/null +++ b/Tests/BehaviorTests/tests/inits/inits.flint @@ -0,0 +1,25 @@ +contract Inits { + var a: Int + var b: Address + var s: String +} + +Inits :: caller <- (any) { + public init(a: Int, s: String) { + self.a = a + self.b = caller + self.s = s + } + + public func getA() -> Int { + return a + } + + public func getB() -> Address { + return b + } + + public func getS() -> String { + return s + } +} diff --git a/Tests/BehaviorTests/tests/inits/test/config.js b/Tests/BehaviorTests/tests/inits/test/config.js new file mode 100644 index 00000000..7b06f323 --- /dev/null +++ b/Tests/BehaviorTests/tests/inits/test/config.js @@ -0,0 +1 @@ +exports.contractName = "Inits" diff --git a/Tests/BehaviorTests/tests/inits/test/migrations/1.js b/Tests/BehaviorTests/tests/inits/test/migrations/1.js new file mode 100644 index 00000000..6bb6741d --- /dev/null +++ b/Tests/BehaviorTests/tests/inits/test/migrations/1.js @@ -0,0 +1,7 @@ +var config = require("../config.js") + +var Contract = artifacts.require("./" + config.contractName + ".sol"); + +module.exports = function(deployer) { + deployer.deploy(Contract, 4, "hello"); +}; diff --git a/Tests/BehaviorTests/tests/inits/test/test/.placeholder b/Tests/BehaviorTests/tests/inits/test/test/.placeholder new file mode 100644 index 00000000..3ed31e18 --- /dev/null +++ b/Tests/BehaviorTests/tests/inits/test/test/.placeholder @@ -0,0 +1 @@ +This is a placeholder file to ensure the parent directory in the git repository. Feel free to remove. diff --git a/Tests/BehaviorTests/tests/inits/test/test/test.js b/Tests/BehaviorTests/tests/inits/test/test/test.js new file mode 100644 index 00000000..1bf2c55d --- /dev/null +++ b/Tests/BehaviorTests/tests/inits/test/test/test.js @@ -0,0 +1,23 @@ +var config = require("../config.js") + +var Contract = artifacts.require("./" + config.contractName + ".sol"); +var Interface = artifacts.require("./_Interface" + config.contractName + ".sol"); +Contract.abi = Interface.abi + +contract(config.contractName, function(accounts) { + it("should correctly set initializer arguments", async function() { + const instance = await Contract.deployed(); + let t; + + t = await instance.getX(); + assert.equal(t.valueOf(), 4); + + t = await instance.getB(); + assert.equal(t.valueOf(), accounts[0]); + + t = await instance.getS(); + assert.equal(web3.toUtf8(t.valueOf()), "hello"); + }); +}); + + diff --git a/Tests/BehaviorTests/tests/inits/test/truffle-config.js b/Tests/BehaviorTests/tests/inits/test/truffle-config.js new file mode 100644 index 00000000..a6330d6d --- /dev/null +++ b/Tests/BehaviorTests/tests/inits/test/truffle-config.js @@ -0,0 +1,4 @@ +module.exports = { + // See + // to customize your Truffle configuration! +}; diff --git a/Tests/BehaviorTests/tests/inits/test/truffle.js b/Tests/BehaviorTests/tests/inits/test/truffle.js new file mode 100644 index 00000000..a6330d6d --- /dev/null +++ b/Tests/BehaviorTests/tests/inits/test/truffle.js @@ -0,0 +1,4 @@ +module.exports = { + // See + // to customize your Truffle configuration! +}; diff --git a/Tests/BehaviorTests/tests/structs/structs.flint b/Tests/BehaviorTests/tests/structs/structs.flint index be34ea6f..258c6eb6 100644 --- a/Tests/BehaviorTests/tests/structs/structs.flint +++ b/Tests/BehaviorTests/tests/structs/structs.flint @@ -34,6 +34,8 @@ contract C { } C :: (any) { + init() {} + public func getAx() -> Int { return a.x } diff --git a/Tests/SemanticTests/multiple_inits.flint b/Tests/SemanticTests/multiple_inits.flint new file mode 100644 index 00000000..5cfe3f02 --- /dev/null +++ b/Tests/SemanticTests/multiple_inits.flint @@ -0,0 +1,18 @@ +// RUN: %flintc %s --verify + +contract Test { + var a: Address +} + +Test :: caller <- (any) { + public init(a: Address) { // expected-note {{A public initializer is already declared here}} + self.a = a + } + + public init() { // expected-error {{A public initializer has already been defined}} + self.a = caller + } + + init() { + } +} From 0874f651a6e873aa149f8babd1a80b12001508e4 Mon Sep 17 00:00:00 2001 From: Franklin Schrans Date: Sat, 28 Apr 2018 13:53:20 +0100 Subject: [PATCH 3/7] Remove commented out code --- Sources/IRGen/Function/IULIAInitializer.swift | 2 -- Sources/IRGen/IULIAContract.swift | 25 ------------------- 2 files changed, 27 deletions(-) diff --git a/Sources/IRGen/Function/IULIAInitializer.swift b/Sources/IRGen/Function/IULIAInitializer.swift index dc6c2ae9..c262f6e9 100644 --- a/Sources/IRGen/Function/IULIAInitializer.swift +++ b/Sources/IRGen/Function/IULIAInitializer.swift @@ -71,7 +71,6 @@ struct IULIAInitializer { func renderDefaultValuesAssignments() -> String { let defaultValueAssignments = propertiesInEnclosingType.compactMap { declaration -> String? in guard let assignedExpression = declaration.assignedExpression else { return nil } -// let offset = environment.propertyOffset(for: declaration.identifier.name, enclosingType: typeIdentifier.name)! guard case .literal(let literalToken) = assignedExpression else { fatalError("Non-literal default values are not supported yet") } @@ -80,7 +79,6 @@ struct IULIAInitializer { identifier.enclosingType = typeIdentifier.name return IULIAAssignment(lhs: .identifier(identifier), rhs: .literal(literalToken)).rendered(functionContext: functionContext) -// return "sstore(\(), \(IULIALiteralToken(literalToken: literalToken).rendered()));" } return defaultValueAssignments.joined(separator: "\n") diff --git a/Sources/IRGen/IULIAContract.swift b/Sources/IRGen/IULIAContract.swift index 9827f87f..0068b7de 100644 --- a/Sources/IRGen/IULIAContract.swift +++ b/Sources/IRGen/IULIAContract.swift @@ -126,31 +126,6 @@ struct IULIAContract { }.joined(separator: "\n") } -// // Generate an initializer which takes in the 256-bit values in storage. -// let initializerParameters = contractDeclaration.variableDeclarations.filter { $0.type.rawType.isBasicType && !$0.type.rawType.isEventType && $0.assignedExpression == nil } -// let initializerParameterList = initializerParameters.map { "\(CanonicalType(from: $0.type.rawType)!.rawValue) \($0.identifier.name)" }.joined(separator: ", ") -// var initializerBody = initializerParameters.map { parameter in -// let offset = environment.propertyOffset(for: parameter.identifier.name, enclosingType: contractDeclaration.identifier.name)! -// return "_flintStorage\(offset) = \(parameter.identifier.name);" -// }.joined(separator: "\n") -// -// let defaultValueAssignments = contractDeclaration.variableDeclarations.compactMap { declaration -> String? in -// guard let assignedExpression = declaration.assignedExpression else { return nil } -// let offset = environment.propertyOffset(for: declaration.identifier.name, enclosingType: contractDeclaration.identifier.name)! -// guard case .literal(let literalToken) = assignedExpression else { -// fatalError("Non-literal default values are not supported yet") -// } -// return "_flintStorage\(offset) = \(IULIALiteralToken(literalToken: literalToken).rendered());" -// } -// -// initializerBody += "\n" + defaultValueAssignments.joined(separator: "\n") -// return """ -// function \(contractDeclaration.identifier.name)(\(initializerParameterList)) public { -// \(initializerBody.indented(by: 2)) -// } -// """ -// } - /// The list of canonical types for the given type. Fixed-size arrays of size `n` will result in a list of `n` /// canonical types. public func storageCanonicalTypes(for type: Type.RawType) -> [String] { From 4dc7422e82dcdc5e868cc92aed681e9d001b3983 Mon Sep 17 00:00:00 2001 From: Franklin Schrans Date: Sat, 28 Apr 2018 14:04:44 +0100 Subject: [PATCH 4/7] Only generate IULIA constructors if init is public --- Sources/IRGen/IULIAContract.swift | 62 +------------------ Tests/BehaviorTests/tests/bank/bank.flint | 2 +- .../BehaviorTests/tests/structs/structs.flint | 2 +- 3 files changed, 5 insertions(+), 61 deletions(-) diff --git a/Sources/IRGen/IULIAContract.swift b/Sources/IRGen/IULIAContract.swift index 0068b7de..b59518c7 100644 --- a/Sources/IRGen/IULIAContract.swift +++ b/Sources/IRGen/IULIAContract.swift @@ -27,7 +27,6 @@ struct IULIAContract { return contractBehaviorDeclaration.members.compactMap { member -> IULIAFunction? in guard case .functionDeclaration(let functionDeclaration) = member else { return nil - // Rendering initializers is not supported yet. } return IULIAFunction(functionDeclaration: functionDeclaration, typeIdentifier: contractDeclaration.identifier, capabilityBinding: contractBehaviorDeclaration.capabilityBinding, callerCapabilities: contractBehaviorDeclaration.callerCapabilities, environment: environment) } @@ -46,24 +45,8 @@ struct IULIAContract { } } - // TODO: Generate code for initializers too. - let structsFunctionsCode = structFunctions.map({ $0.rendered() }).joined(separator: "\n\n").indented(by: 6) - let initializerBody = renderInitializers() - - var index = 0 - var propertyDeclarations = [String]() - - for property in contractDeclaration.variableDeclarations where !property.type.rawType.isEventType { - let rawType = property.type.rawType - - for canonicalType in storageCanonicalTypes(for: rawType) { - propertyDeclarations.append("\(canonicalType) _flintStorage\(index);") - index += 1 - } - } - - let propertyDeclarationsCode = propertyDeclarations.joined(separator: "\n") + let initializerBody = renderPublicInitializers() // Generate runtime functions. let runtimeFunctionsDeclarations = IULIARuntimeFunction.all.map { $0.declaration }.joined(separator: "\n\n").indented(by: 6) @@ -74,8 +57,6 @@ struct IULIAContract { contract \(contractDeclaration.identifier.name) { - \(propertyDeclarationsCode.indented(by: 2)) - \(initializerBody.indented(by: 2)) function () public payable { @@ -99,10 +80,10 @@ struct IULIAContract { """ } - func renderInitializers() -> String { + func renderPublicInitializers() -> String { return contractBehaviorDeclarations.flatMap { contractBehaviorDeclaration in return contractBehaviorDeclaration.members.compactMap { member -> String? in - guard case .initializerDeclaration(let initializerDeclaration) = member else { + guard case .initializerDeclaration(let initializerDeclaration) = member, initializerDeclaration.isPublic else { return nil } @@ -113,8 +94,6 @@ struct IULIAContract { return "\(CanonicalType(from: parameter.type.rawType)!.rawValue) \(parameterName)" }.joined(separator: ", ") - // TODO: Assign default values set at property declarations. - return """ constructor(\(parameters)) public { assembly { @@ -125,39 +104,4 @@ struct IULIAContract { } }.joined(separator: "\n") } - - /// The list of canonical types for the given type. Fixed-size arrays of size `n` will result in a list of `n` - /// canonical types. - public func storageCanonicalTypes(for type: Type.RawType) -> [String] { - switch type { - case .builtInType(_), .arrayType(_), .dictionaryType(_, _): - return [type.canonicalElementType!.rawValue] - case .fixedSizeArrayType(let rawType, let elementCount): - return [String](repeating: rawType.canonicalElementType!.rawValue, count: elementCount) - case .errorType: fatalError() - - case .userDefinedType(let identifier): - return environment.properties(in: identifier).flatMap { property -> [String] in - let type = environment.type(of: property, enclosingType: identifier)! - return storageCanonicalTypes(for: type) - } - case .inoutType(_): fatalError() - } - } -} - -fileprivate extension Type.RawType { - - /// The canonical type of self, or its element's canonical type in the case of arrays and dictionaries. - var canonicalElementType: CanonicalType? { - switch self { - case .builtInType(_): return CanonicalType(from: self) - case .errorType: return CanonicalType(from: self) - case .dictionaryType(_, _): return .uint256 // Nothing is stored in that property. - case .arrayType(_): return .uint256 // The number of elements is stored. - case .fixedSizeArrayType(let elementType, _): return CanonicalType(from: elementType) - case .userDefinedType(_): fatalError() - case .inoutType(_): fatalError() - } - } } diff --git a/Tests/BehaviorTests/tests/bank/bank.flint b/Tests/BehaviorTests/tests/bank/bank.flint index 5c383da2..f429bd90 100644 --- a/Tests/BehaviorTests/tests/bank/bank.flint +++ b/Tests/BehaviorTests/tests/bank/bank.flint @@ -8,7 +8,7 @@ contract Bank { } Bank :: account <- (any) { - init(manager: Address) { + public init(manager: Address) { self.manager = manager } diff --git a/Tests/BehaviorTests/tests/structs/structs.flint b/Tests/BehaviorTests/tests/structs/structs.flint index 258c6eb6..6df44dba 100644 --- a/Tests/BehaviorTests/tests/structs/structs.flint +++ b/Tests/BehaviorTests/tests/structs/structs.flint @@ -34,7 +34,7 @@ contract C { } C :: (any) { - init() {} + public init() {} public func getAx() -> Int { return a.x From adad6eb4f20641f972c7c49d204324b50c2365ca Mon Sep 17 00:00:00 2001 From: Franklin Schrans Date: Sat, 28 Apr 2018 17:11:34 +0100 Subject: [PATCH 5/7] Synthesize public initializer if none is specified --- Sources/AST/Environment.swift | 11 +++ .../IULIACallerCapabilityChecks.swift | 44 +++++++++ Sources/IRGen/Function/IULIAFunction.swift | 36 ------- Sources/IRGen/IULIAContract.swift | 65 ++++++++---- .../SemanticAnalyzer/SemanticAnalyzer.swift | 47 ++++----- Sources/flintc/ASTPassRunner.swift | 3 +- Sources/flintc/Compiler.swift | 2 +- .../tests/array/test/migrations/1.js | 2 +- .../tests/dictionary/test/migrations/1.js | 2 +- .../tests/factorial/test/migrations/1.js | 2 +- Tests/BehaviorTests/tests/string/string.flint | 4 + .../tests/string/test/migrations/1.js | 2 +- tmp | 99 +++++++++++++++++++ tmp2 | 96 ++++++++++++++++++ 14 files changed, 325 insertions(+), 90 deletions(-) create mode 100644 Sources/IRGen/Function/IULIACallerCapabilityChecks.swift create mode 100644 tmp create mode 100644 tmp2 diff --git a/Sources/AST/Environment.swift b/Sources/AST/Environment.swift index 5831896d..4cbd9617 100644 --- a/Sources/AST/Environment.swift +++ b/Sources/AST/Environment.swift @@ -234,6 +234,16 @@ public struct Environment { return .failure(candidates: candidates) } + /// Set the public initializer for the given contract. A contract should have at most one public initializer. + public mutating func setPublicInitializer(_ publicInitializer: InitializerDeclaration, forContract contract: RawTypeIdentifier) { + types[contract]!.publicInitializer = publicInitializer + } + + /// The public initializer for the given contract. A contract should have at most one public initializer. + public func publicInitializer(forContract contract: RawTypeIdentifier) -> InitializerDeclaration? { + return types[contract]!.publicInitializer + } + /// Whether two caller capability groups are compatible, i.e. whether a function with caller capabilities `source` is /// able to call a function which require caller capabilities `target`. func areCallerCapabilitiesCompatible(source: [CallerCapability], target: [CallerCapability]) -> Bool { @@ -309,6 +319,7 @@ public struct TypeInformation { var orderedProperties = [String]() var properties = [String: PropertyInformation]() var functions = [String: [FunctionInformation]]() + var publicInitializer: InitializerDeclaration? = nil } /// Information about a property defined in a type, such as its type and generic arguments. diff --git a/Sources/IRGen/Function/IULIACallerCapabilityChecks.swift b/Sources/IRGen/Function/IULIACallerCapabilityChecks.swift new file mode 100644 index 00000000..2df7952f --- /dev/null +++ b/Sources/IRGen/Function/IULIACallerCapabilityChecks.swift @@ -0,0 +1,44 @@ +// +// IULIACallerCapabilityChecks.swift +// IRGen +// +// Created by Franklin Schrans on 4/28/18. +// + +import AST + +/// Checks whether the caller of a function has appropriate caller capabilities. +struct IULIACallerCapabilityChecks { + var callerCapabilities: [CallerCapability] + + func rendered(functionContext: FunctionContext) -> String { + let environment = functionContext.environment + + let checks = callerCapabilities.compactMap { callerCapability -> String? in + guard !callerCapability.isAny else { return nil } + + let type = environment.type(of: callerCapability.identifier.name, enclosingType: functionContext.enclosingTypeName)! + let offset = environment.propertyOffset(for: callerCapability.name, enclosingType: functionContext.enclosingTypeName)! + switch type { + case .fixedSizeArrayType(_, let size): + return (0.. String { - let environment = functionContext.environment - - let checks = callerCapabilities.compactMap { callerCapability -> String? in - guard !callerCapability.isAny else { return nil } - - let type = environment.type(of: callerCapability.identifier.name, enclosingType: functionContext.enclosingTypeName)! - let offset = environment.propertyOffset(for: callerCapability.name, enclosingType: functionContext.enclosingTypeName)! - switch type { - case .fixedSizeArrayType(_, let size): - return (0.. String { - return contractBehaviorDeclarations.flatMap { contractBehaviorDeclaration in - return contractBehaviorDeclaration.members.compactMap { member -> String? in - guard case .initializerDeclaration(let initializerDeclaration) = member, initializerDeclaration.isPublic else { - return nil - } + func renderPublicInitializer() -> String { + let initializerDeclaration: InitializerDeclaration - let initializer = IULIAInitializer(initializerDeclaration: initializerDeclaration, typeIdentifier: contractDeclaration.identifier, propertiesInEnclosingType: contractDeclaration.variableDeclarations, capabilityBinding: contractBehaviorDeclaration.capabilityBinding, callerCapabilities: contractBehaviorDeclaration.callerCapabilities, environment: environment, isContractFunction: true).rendered() + // The contract behavior declaration the initializer resides in. + let contractBehaviorDeclaration: ContractBehaviorDeclaration? - let parameters = initializerDeclaration.parameters.map { parameter in - let parameterName = Mangler.mangleName(parameter.identifier.name) - return "\(CanonicalType(from: parameter.type.rawType)!.rawValue) \(parameterName)" - }.joined(separator: ", ") + if let publicInitializer = environment.publicInitializer(forContract: contractDeclaration.identifier.name) { + initializerDeclaration = publicInitializer + contractBehaviorDeclaration = findEnclosingContractBehaviorDeclaration(forInitializer: initializerDeclaration)! + } else { + // If there is not public initializer defined, synthesize one. + initializerDeclaration = synthesizeInitializer() + contractBehaviorDeclaration = nil + } - return """ - constructor(\(parameters)) public { - assembly { - \(initializer.indented(by: 4)) - } - } - """ + let capabilityBinding = contractBehaviorDeclaration?.capabilityBinding + let callerCapabilities = contractBehaviorDeclaration?.callerCapabilities ?? [] + + let initializer = IULIAInitializer(initializerDeclaration: initializerDeclaration, typeIdentifier: contractDeclaration.identifier, propertiesInEnclosingType: contractDeclaration.variableDeclarations, capabilityBinding: capabilityBinding, callerCapabilities: callerCapabilities, environment: environment, isContractFunction: true).rendered() + + let parameters = initializerDeclaration.parameters.map { parameter in + let parameterName = Mangler.mangleName(parameter.identifier.name) + return "\(CanonicalType(from: parameter.type.rawType)!.rawValue) \(parameterName)" + }.joined(separator: ", ") + + return """ + constructor(\(parameters)) public { + assembly { + \(initializer.indented(by: 4)) } - }.joined(separator: "\n") + } + """ + } + + func synthesizeInitializer() -> InitializerDeclaration { + let sourceLocation = contractDeclaration.sourceLocation + + return InitializerDeclaration(initToken: Token(kind: .init, sourceLocation: sourceLocation), attributes: [], modifiers: [Token(kind: .public, sourceLocation: sourceLocation)], parameters: [], closeBracketToken: Token(kind: .punctuation(.closeBracket), sourceLocation: sourceLocation), body: [], closeBraceToken: Token(kind: .punctuation(.closeBrace), sourceLocation: sourceLocation)) + } + + /// Finds the contract behavior declaration associated with the given initializer. A contract should only have one + /// public initializer. + func findEnclosingContractBehaviorDeclaration(forInitializer initializer: InitializerDeclaration) -> ContractBehaviorDeclaration? { + return contractBehaviorDeclarations.first { contractBehaviorDeclaration -> Bool in + return contractBehaviorDeclaration.members.contains { member in + return member == .initializerDeclaration(initializer) + } + } } } diff --git a/Sources/SemanticAnalyzer/SemanticAnalyzer.swift b/Sources/SemanticAnalyzer/SemanticAnalyzer.swift index 091fcfa6..629ee1e4 100644 --- a/Sources/SemanticAnalyzer/SemanticAnalyzer.swift +++ b/Sources/SemanticAnalyzer/SemanticAnalyzer.swift @@ -127,17 +127,7 @@ public struct SemanticAnalyzer: ASTPass { } public func process(initializerDeclaration: InitializerDeclaration, passContext: ASTPassContext) -> ASTPassResult { - var diagnostics = [Diagnostic]() - var passContext = passContext - - if initializerDeclaration.isPublic { - if let publicInitializerSourceLocation = passContext.publicInitializerSourceLocation { - diagnostics.append(.multiplePublicInitializersDefined(initializerDeclaration, originalInitializerLocation: publicInitializerSourceLocation)) - } else { - passContext.publicInitializerSourceLocation = initializerDeclaration.sourceLocation - } - } - return ASTPassResult(element: initializerDeclaration, diagnostics: diagnostics, passContext: passContext) + return ASTPassResult(element: initializerDeclaration, diagnostics: [], passContext: passContext) } public func process(attribute: Attribute, passContext: ASTPassContext) -> ASTPassResult { @@ -304,9 +294,6 @@ public struct SemanticAnalyzer: ASTPass { } public func postProcess(contractBehaviorDeclaration: ContractBehaviorDeclaration, passContext: ASTPassContext) -> ASTPassResult { - let passContext = passContext.withUpdates { - $0.publicInitializerSourceLocation = nil - } return ASTPassResult(element: contractBehaviorDeclaration, diagnostics: [], passContext: passContext) } @@ -315,9 +302,6 @@ public struct SemanticAnalyzer: ASTPass { } public func postProcess(structDeclaration: StructDeclaration, passContext: ASTPassContext) -> ASTPassResult { - let passContext = passContext.withUpdates { - $0.publicInitializerSourceLocation = nil - } return ASTPassResult(element: structDeclaration, diagnostics: [], passContext: passContext) } @@ -346,7 +330,24 @@ public struct SemanticAnalyzer: ASTPass { } public func postProcess(initializerDeclaration: InitializerDeclaration, passContext: ASTPassContext) -> ASTPassResult { - return ASTPassResult(element: initializerDeclaration, diagnostics: [], passContext: passContext) + var diagnostics = [Diagnostic]() + var passContext = passContext + + var environmment = passContext.environment! + + // If we are in a contract behavior declaration, check there is only one public initializer. + if let context = passContext.contractBehaviorDeclarationContext, initializerDeclaration.isPublic { + let contractName = context.contractIdentifier.name + if let publicInitializer = environmment.publicInitializer(forContract: contractName) { + diagnostics.append(.multiplePublicInitializersDefined(initializerDeclaration, originalInitializerLocation: publicInitializer.sourceLocation)) + } else { + // This is the first public initializer we encounter in this contract. + environmment.setPublicInitializer(initializerDeclaration, forContract: contractName) + } + } + + passContext.environment = environmment + return ASTPassResult(element: initializerDeclaration, diagnostics: diagnostics, passContext: passContext) } public func postProcess(attribute: Attribute, passContext: ASTPassContext) -> ASTPassResult { @@ -460,18 +461,8 @@ extension ASTPassContext { get { return self[MutatingExpressionContextEntry.self] } set { self[MutatingExpressionContextEntry.self] = newValue } } - - /// The source location of the public initializer in a type, if one has been declared. - public var publicInitializerSourceLocation: SourceLocation? { - get { return self[PublicInitializerSourceLocation.self] } - set { self[PublicInitializerSourceLocation.self] = newValue } - } } struct MutatingExpressionContextEntry: PassContextEntry { typealias Value = [Expression] } - -struct PublicInitializerSourceLocation: PassContextEntry { - typealias Value = SourceLocation -} diff --git a/Sources/flintc/ASTPassRunner.swift b/Sources/flintc/ASTPassRunner.swift index 46c9e17e..05014d30 100644 --- a/Sources/flintc/ASTPassRunner.swift +++ b/Sources/flintc/ASTPassRunner.swift @@ -29,7 +29,7 @@ struct ASTPassRunner { diagnostics.append(contentsOf: result.diagnostics) } - return ASTPassRunResult(element: ast, diagnostics: diagnostics) + return ASTPassRunResult(element: ast, diagnostics: diagnostics, environment: environment) } } @@ -37,4 +37,5 @@ struct ASTPassRunner { struct ASTPassRunResult { var element: TopLevelModule var diagnostics: [Diagnostic] + var environment: Environment } diff --git a/Sources/flintc/Compiler.swift b/Sources/flintc/Compiler.swift index 6510381c..08cab61a 100644 --- a/Sources/flintc/Compiler.swift +++ b/Sources/flintc/Compiler.swift @@ -68,7 +68,7 @@ struct Compiler { } // Generate IULIA IR code. - let irCode = IULIACodeGenerator(topLevelModule: passRunnerOutcome.element, environment: environment).generateCode() + let irCode = IULIACodeGenerator(topLevelModule: passRunnerOutcome.element, environment: passRunnerOutcome.environment).generateCode() // Compile the IULIA IR code using solc. SolcCompiler(inputSource: irCode, outputDirectory: outputDirectory, emitBytecode: emitBytecode).compile() diff --git a/Tests/BehaviorTests/tests/array/test/migrations/1.js b/Tests/BehaviorTests/tests/array/test/migrations/1.js index 604a4b85..38a8b644 100644 --- a/Tests/BehaviorTests/tests/array/test/migrations/1.js +++ b/Tests/BehaviorTests/tests/array/test/migrations/1.js @@ -3,5 +3,5 @@ var config = require("../config.js") var Contract = artifacts.require("./" + config.contractName + ".sol"); module.exports = function(deployer) { - deployer.deploy(Contract, 0); + deployer.deploy(Contract); }; diff --git a/Tests/BehaviorTests/tests/dictionary/test/migrations/1.js b/Tests/BehaviorTests/tests/dictionary/test/migrations/1.js index d42bcd3c..38a8b644 100644 --- a/Tests/BehaviorTests/tests/dictionary/test/migrations/1.js +++ b/Tests/BehaviorTests/tests/dictionary/test/migrations/1.js @@ -3,5 +3,5 @@ var config = require("../config.js") var Contract = artifacts.require("./" + config.contractName + ".sol"); module.exports = function(deployer) { - deployer.deploy(Contract, 0, 0); + deployer.deploy(Contract); }; diff --git a/Tests/BehaviorTests/tests/factorial/test/migrations/1.js b/Tests/BehaviorTests/tests/factorial/test/migrations/1.js index 604a4b85..38a8b644 100644 --- a/Tests/BehaviorTests/tests/factorial/test/migrations/1.js +++ b/Tests/BehaviorTests/tests/factorial/test/migrations/1.js @@ -3,5 +3,5 @@ var config = require("../config.js") var Contract = artifacts.require("./" + config.contractName + ".sol"); module.exports = function(deployer) { - deployer.deploy(Contract, 0); + deployer.deploy(Contract); }; diff --git a/Tests/BehaviorTests/tests/string/string.flint b/Tests/BehaviorTests/tests/string/string.flint index 65e57f92..9932b8f0 100644 --- a/Tests/BehaviorTests/tests/string/string.flint +++ b/Tests/BehaviorTests/tests/string/string.flint @@ -3,6 +3,10 @@ contract StringContract { } StringContract :: (any) { + public init(s: String) { + self.s = s + } + mutating public func set(s: String) { self.s = s } diff --git a/Tests/BehaviorTests/tests/string/test/migrations/1.js b/Tests/BehaviorTests/tests/string/test/migrations/1.js index 604a4b85..f3a96e74 100644 --- a/Tests/BehaviorTests/tests/string/test/migrations/1.js +++ b/Tests/BehaviorTests/tests/string/test/migrations/1.js @@ -3,5 +3,5 @@ var config = require("../config.js") var Contract = artifacts.require("./" + config.contractName + ".sol"); module.exports = function(deployer) { - deployer.deploy(Contract, 0); + deployer.deploy(Contract, "hello"); }; diff --git a/tmp b/tmp new file mode 100644 index 00000000..623f9730 --- /dev/null +++ b/tmp @@ -0,0 +1,99 @@ +InitializerDeclaration +▿ initToken : Token +- kind : init +▿ sourceLocation : SourceLocation +- line : 11 +- column : 10 +- length : 4 +- attributes : 0 elements +▿ modifiers : 1 element +▿ 0 : Token +- kind : public +▿ sourceLocation : SourceLocation +- line : 11 +- column : 3 +- length : 6 +▿ parameters : 1 element +▿ 0 : Parameter +▿ identifier : Identifier +▿ identifierToken : Token +▿ kind : identifier "manager" +- identifier : "manager" +▿ sourceLocation : SourceLocation +- line : 11 +- column : 15 +- length : 7 +- enclosingType : nil +▿ type : Type +▿ rawType : RawType +- builtInType : AST.Type.BuiltInType.address +- genericArguments : 0 elements +▿ sourceLocation : SourceLocation +- line : 11 +- column : 24 +- length : 7 +- implicitToken : nil +▿ closeBracketToken : Token +▿ kind : ) +- punctuation : AST.Token.Kind.Punctuation.closeBracket +▿ sourceLocation : SourceLocation +- line : 11 +- column : 31 +- length : 1 +▿ body : 1 element +▿ 0 : Statement +▿ expression : Expression +▿ binaryExpression : BinaryExpression +▿ lhs : Expression +▿ binaryExpression : BinaryExpression +▿ lhs : Expression +▿ self : Token +- kind : self +▿ sourceLocation : SourceLocation +- line : 12 +- column : 5 +- length : 4 +▿ op : Token +▿ kind : . +- punctuation : AST.Token.Kind.Punctuation.dot +▿ sourceLocation : SourceLocation +- line : 12 +- column : 9 +- length : 1 +▿ rhs : Expression +▿ identifier : Identifier +▿ identifierToken : Token +▿ kind : identifier "manager" +- identifier : "manager" +▿ sourceLocation : SourceLocation +- line : 12 +- column : 10 +- length : 7 +▿ enclosingType : Optional +- some : "Bank" +▿ op : Token +▿ kind : = +- punctuation : AST.Token.Kind.Punctuation.equal +▿ sourceLocation : SourceLocation +- line : 12 +- column : 18 +- length : 1 +▿ rhs : Expression +▿ identifier : Identifier +▿ identifierToken : Token +▿ kind : identifier "manager" +- identifier : "manager" +▿ sourceLocation : SourceLocation +- line : 12 +- column : 20 +- length : 7 +- enclosingType : nil +▿ closeBraceToken : Token +▿ kind : ) +- punctuation : AST.Token.Kind.Punctuation.closeBracket +▿ sourceLocation : SourceLocation +- line : 11 +- column : 31 +- length : 1 + +(lldb) diff --git a/tmp2 b/tmp2 new file mode 100644 index 00000000..02a4b3b0 --- /dev/null +++ b/tmp2 @@ -0,0 +1,96 @@ +InitializerDeclaration +▿ initToken : Token +- kind : init +▿ sourceLocation : SourceLocation +- line : 11 +- column : 10 +- length : 4 +- attributes : 0 elements +▿ modifiers : 1 element +▿ 0 : Token +- kind : public +▿ sourceLocation : SourceLocation +- line : 11 +- column : 3 +- length : 6 +▿ parameters : 1 element +▿ 0 : Parameter +▿ identifier : Identifier +▿ identifierToken : Token +▿ kind : identifier "manager" +- identifier : "manager" +▿ sourceLocation : SourceLocation +- line : 11 +- column : 15 +- length : 7 +- enclosingType : nil +▿ type : Type +▿ rawType : RawType +- builtInType : AST.Type.BuiltInType.address +- genericArguments : 0 elements +▿ sourceLocation : SourceLocation +- line : 11 +- column : 24 +- length : 7 +- implicitToken : nil +▿ closeBracketToken : Token +▿ kind : ) +- punctuation : AST.Token.Kind.Punctuation.closeBracket +▿ sourceLocation : SourceLocation +- line : 11 +- column : 31 +- length : 1 +▿ body : 1 element +▿ 0 : Statement +▿ expression : Expression +▿ binaryExpression : BinaryExpression +▿ lhs : Expression +▿ binaryExpression : BinaryExpression +▿ lhs : Expression +▿ self : Token +- kind : self +▿ sourceLocation : SourceLocation +- line : 12 +- column : 5 +- length : 4 +▿ op : Token +▿ kind : . +- punctuation : AST.Token.Kind.Punctuation.dot +▿ sourceLocation : SourceLocation +- line : 12 +- column : 9 +- length : 1 +▿ rhs : Expression +▿ identifier : Identifier +▿ identifierToken : Token +▿ kind : identifier "manager" +- identifier : "manager" +▿ sourceLocation : SourceLocation +- line : 12 +- column : 10 +- length : 7 +- enclosingType : nil +▿ op : Token +▿ kind : = +- punctuation : AST.Token.Kind.Punctuation.equal +▿ sourceLocation : SourceLocation +- line : 12 +- column : 18 +- length : 1 +▿ rhs : Expression +▿ identifier : Identifier +▿ identifierToken : Token +▿ kind : identifier "manager" +- identifier : "manager" +▿ sourceLocation : SourceLocation +- line : 12 +- column : 20 +- length : 7 +- enclosingType : nil +▿ closeBraceToken : Token +▿ kind : ) +- punctuation : AST.Token.Kind.Punctuation.closeBracket +▿ sourceLocation : SourceLocation +- line : 11 +- column : 31 +- length : 1 From aa8b1a70b6972498daec405b8ae1bde093e36aea Mon Sep 17 00:00:00 2001 From: Franklin Schrans Date: Sat, 28 Apr 2018 22:14:39 +0100 Subject: [PATCH 6/7] Disallow inits in blocks scoped by "any" --- Sources/SemanticAnalyzer/SemanticAnalyzer.swift | 14 ++++++++++---- Sources/SemanticAnalyzer/SemanticError.swift | 4 ++++ .../{multiple_inits.flint => inits.flint} | 5 +++++ 3 files changed, 19 insertions(+), 4 deletions(-) rename Tests/SemanticTests/{multiple_inits.flint => inits.flint} (69%) diff --git a/Sources/SemanticAnalyzer/SemanticAnalyzer.swift b/Sources/SemanticAnalyzer/SemanticAnalyzer.swift index 629ee1e4..0df3a8a8 100644 --- a/Sources/SemanticAnalyzer/SemanticAnalyzer.swift +++ b/Sources/SemanticAnalyzer/SemanticAnalyzer.swift @@ -338,11 +338,17 @@ public struct SemanticAnalyzer: ASTPass { // If we are in a contract behavior declaration, check there is only one public initializer. if let context = passContext.contractBehaviorDeclarationContext, initializerDeclaration.isPublic { let contractName = context.contractIdentifier.name - if let publicInitializer = environmment.publicInitializer(forContract: contractName) { - diagnostics.append(.multiplePublicInitializersDefined(initializerDeclaration, originalInitializerLocation: publicInitializer.sourceLocation)) + + // The caller capability block in which this initializer appears should be scoped by "any". + if !context.callerCapabilities.contains(where: { $0.isAny }) { + diagnostics.append(.contractInitializerNotDeclaredInAnyCallerCapabilityBlock(initializerDeclaration)) } else { - // This is the first public initializer we encounter in this contract. - environmment.setPublicInitializer(initializerDeclaration, forContract: contractName) + if let publicInitializer = environmment.publicInitializer(forContract: contractName) { + diagnostics.append(.multiplePublicInitializersDefined(initializerDeclaration, originalInitializerLocation: publicInitializer.sourceLocation)) + } else { + // This is the first public initializer we encounter in this contract. + environmment.setPublicInitializer(initializerDeclaration, forContract: contractName) + } } } diff --git a/Sources/SemanticAnalyzer/SemanticError.swift b/Sources/SemanticAnalyzer/SemanticError.swift index e78c7ef1..30321e26 100644 --- a/Sources/SemanticAnalyzer/SemanticError.swift +++ b/Sources/SemanticAnalyzer/SemanticError.swift @@ -65,6 +65,10 @@ extension Diagnostic { return Diagnostic(severity: .error, sourceLocation: invalidAdditionalInitializer.sourceLocation, message: "A public initializer has already been defined", notes: [note]) } + static func contractInitializerNotDeclaredInAnyCallerCapabilityBlock(_ initializerDeclaration: InitializerDeclaration) -> Diagnostic { + return Diagnostic(severity: .error, sourceLocation: initializerDeclaration.sourceLocation, message: "Public contract initializer should be callable using caller capability \"any\"") + } + static func renderCapabilityGroup(_ capabilities: [CallerCapability]) -> String { return "(\(capabilities.map({ $0.name }).joined(separator: ", ")))" } diff --git a/Tests/SemanticTests/multiple_inits.flint b/Tests/SemanticTests/inits.flint similarity index 69% rename from Tests/SemanticTests/multiple_inits.flint rename to Tests/SemanticTests/inits.flint index 5cfe3f02..e7072766 100644 --- a/Tests/SemanticTests/multiple_inits.flint +++ b/Tests/SemanticTests/inits.flint @@ -16,3 +16,8 @@ Test :: caller <- (any) { init() { } } + +Test :: (a) { + public init(a: Address) { // expected-error {{Public contract initializer should be callable using caller capability "any"}} + } +} From 261595661252d8cc43f5ea3618d28ffe62a376a1 Mon Sep 17 00:00:00 2001 From: Franklin Schrans Date: Sat, 28 Apr 2018 22:15:35 +0100 Subject: [PATCH 7/7] Remove tmp files --- tmp | 99 ------------------------------------------------------------ tmp2 | 96 ---------------------------------------------------------- 2 files changed, 195 deletions(-) delete mode 100644 tmp delete mode 100644 tmp2 diff --git a/tmp b/tmp deleted file mode 100644 index 623f9730..00000000 --- a/tmp +++ /dev/null @@ -1,99 +0,0 @@ -InitializerDeclaration -▿ initToken : Token -- kind : init -▿ sourceLocation : SourceLocation -- line : 11 -- column : 10 -- length : 4 -- attributes : 0 elements -▿ modifiers : 1 element -▿ 0 : Token -- kind : public -▿ sourceLocation : SourceLocation -- line : 11 -- column : 3 -- length : 6 -▿ parameters : 1 element -▿ 0 : Parameter -▿ identifier : Identifier -▿ identifierToken : Token -▿ kind : identifier "manager" -- identifier : "manager" -▿ sourceLocation : SourceLocation -- line : 11 -- column : 15 -- length : 7 -- enclosingType : nil -▿ type : Type -▿ rawType : RawType -- builtInType : AST.Type.BuiltInType.address -- genericArguments : 0 elements -▿ sourceLocation : SourceLocation -- line : 11 -- column : 24 -- length : 7 -- implicitToken : nil -▿ closeBracketToken : Token -▿ kind : ) -- punctuation : AST.Token.Kind.Punctuation.closeBracket -▿ sourceLocation : SourceLocation -- line : 11 -- column : 31 -- length : 1 -▿ body : 1 element -▿ 0 : Statement -▿ expression : Expression -▿ binaryExpression : BinaryExpression -▿ lhs : Expression -▿ binaryExpression : BinaryExpression -▿ lhs : Expression -▿ self : Token -- kind : self -▿ sourceLocation : SourceLocation -- line : 12 -- column : 5 -- length : 4 -▿ op : Token -▿ kind : . -- punctuation : AST.Token.Kind.Punctuation.dot -▿ sourceLocation : SourceLocation -- line : 12 -- column : 9 -- length : 1 -▿ rhs : Expression -▿ identifier : Identifier -▿ identifierToken : Token -▿ kind : identifier "manager" -- identifier : "manager" -▿ sourceLocation : SourceLocation -- line : 12 -- column : 10 -- length : 7 -▿ enclosingType : Optional -- some : "Bank" -▿ op : Token -▿ kind : = -- punctuation : AST.Token.Kind.Punctuation.equal -▿ sourceLocation : SourceLocation -- line : 12 -- column : 18 -- length : 1 -▿ rhs : Expression -▿ identifier : Identifier -▿ identifierToken : Token -▿ kind : identifier "manager" -- identifier : "manager" -▿ sourceLocation : SourceLocation -- line : 12 -- column : 20 -- length : 7 -- enclosingType : nil -▿ closeBraceToken : Token -▿ kind : ) -- punctuation : AST.Token.Kind.Punctuation.closeBracket -▿ sourceLocation : SourceLocation -- line : 11 -- column : 31 -- length : 1 - -(lldb) diff --git a/tmp2 b/tmp2 deleted file mode 100644 index 02a4b3b0..00000000 --- a/tmp2 +++ /dev/null @@ -1,96 +0,0 @@ -InitializerDeclaration -▿ initToken : Token -- kind : init -▿ sourceLocation : SourceLocation -- line : 11 -- column : 10 -- length : 4 -- attributes : 0 elements -▿ modifiers : 1 element -▿ 0 : Token -- kind : public -▿ sourceLocation : SourceLocation -- line : 11 -- column : 3 -- length : 6 -▿ parameters : 1 element -▿ 0 : Parameter -▿ identifier : Identifier -▿ identifierToken : Token -▿ kind : identifier "manager" -- identifier : "manager" -▿ sourceLocation : SourceLocation -- line : 11 -- column : 15 -- length : 7 -- enclosingType : nil -▿ type : Type -▿ rawType : RawType -- builtInType : AST.Type.BuiltInType.address -- genericArguments : 0 elements -▿ sourceLocation : SourceLocation -- line : 11 -- column : 24 -- length : 7 -- implicitToken : nil -▿ closeBracketToken : Token -▿ kind : ) -- punctuation : AST.Token.Kind.Punctuation.closeBracket -▿ sourceLocation : SourceLocation -- line : 11 -- column : 31 -- length : 1 -▿ body : 1 element -▿ 0 : Statement -▿ expression : Expression -▿ binaryExpression : BinaryExpression -▿ lhs : Expression -▿ binaryExpression : BinaryExpression -▿ lhs : Expression -▿ self : Token -- kind : self -▿ sourceLocation : SourceLocation -- line : 12 -- column : 5 -- length : 4 -▿ op : Token -▿ kind : . -- punctuation : AST.Token.Kind.Punctuation.dot -▿ sourceLocation : SourceLocation -- line : 12 -- column : 9 -- length : 1 -▿ rhs : Expression -▿ identifier : Identifier -▿ identifierToken : Token -▿ kind : identifier "manager" -- identifier : "manager" -▿ sourceLocation : SourceLocation -- line : 12 -- column : 10 -- length : 7 -- enclosingType : nil -▿ op : Token -▿ kind : = -- punctuation : AST.Token.Kind.Punctuation.equal -▿ sourceLocation : SourceLocation -- line : 12 -- column : 18 -- length : 1 -▿ rhs : Expression -▿ identifier : Identifier -▿ identifierToken : Token -▿ kind : identifier "manager" -- identifier : "manager" -▿ sourceLocation : SourceLocation -- line : 12 -- column : 20 -- length : 7 -- enclosingType : nil -▿ closeBraceToken : Token -▿ kind : ) -- punctuation : AST.Token.Kind.Punctuation.closeBracket -▿ sourceLocation : SourceLocation -- line : 11 -- column : 31 -- length : 1