Skip to content

Commit

Permalink
Revert "External call basic semantic checks (#98)" (#120)
Browse files Browse the repository at this point in the history
This reverts commit 2cdb48f.
  • Loading branch information
nvgrw authored Nov 25, 2018
1 parent 2cdb48f commit d757f29
Show file tree
Hide file tree
Showing 14 changed files with 54 additions and 160 deletions.
1 change: 1 addition & 0 deletions Sources/AST/ASTDumper.swift
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,7 @@ public class ASTDumper {
case .forStatement(let forStatement): self.dump(forStatement)
case .emitStatement(let emitStatement): self.dump(emitStatement)
case .doCatchStatement(let doCatchStatement): self.dump(doCatchStatement)
case .externalCall(let externalCall): self.dump(externalCall)
}
}
}
Expand Down
10 changes: 0 additions & 10 deletions Sources/AST/ASTPass/ASTPassContext.swift
Original file line number Diff line number Diff line change
Expand Up @@ -156,12 +156,6 @@ extension ASTPassContext {
set { self[ExternalCallContext.self] = newValue }
}

/// When inside a do-block, this property is set to `true`.
public var doBlockNestingCount: Int {
get { return self[DoBlockNestingCount.self] ?? 0 }
set { self[DoBlockNestingCount.self] = newValue }
}

/// When visiting argument labels in a function call, this property is set to `true`.
public var isFunctionCallArgumentLabel: Bool {
get { return self[IsFunctionCallArgumentLabel.self] ?? false }
Expand Down Expand Up @@ -300,10 +294,6 @@ private struct IsExternalFunctionCallContextEntry: PassContextEntry {
typealias Value = Bool
}

private struct DoBlockNestingCount: PassContextEntry {
typealias Value = Int
}

private struct IsAssignment: PassContextEntry {
typealias Value = Bool
}
Expand Down
5 changes: 3 additions & 2 deletions Sources/AST/ASTVisitor/ASTVisitor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,9 @@ public struct ASTVisitor {
case .doCatchStatement(let doCatchStatement):
processResult.element =
.doCatchStatement(processResult.combining(visit(doCatchStatement, passContext: processResult.passContext)))
case .externalCall(let externalCall):
processResult.element =
.externalCall(processResult.combining(visit(externalCall, passContext: processResult.passContext)))
}
let postProcessResult = pass.postProcess(statement: processResult.element, passContext: processResult.passContext)
return ASTPassResult(element: postProcessResult.element,
Expand Down Expand Up @@ -518,12 +521,10 @@ public struct ASTVisitor {
var passContext = passContext
var processResult = pass.process(doCatchStatement: doCatchStatement, passContext: passContext)

processResult.passContext.doBlockNestingCount += 1
let scopeContext = passContext.scopeContext
processResult.element.doBody = processResult.element.doBody.map { statement in
return processResult.combining(visit(statement, passContext: processResult.passContext))
}
processResult.passContext.doBlockNestingCount -= 1

processResult.passContext.scopeContext = scopeContext
processResult.element.catchBody = processResult.element.catchBody.map { statement in
Expand Down
3 changes: 3 additions & 0 deletions Sources/AST/Statement/Statement.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ public indirect enum Statement: ASTNode {
case forStatement(ForStatement)
case emitStatement(EmitStatement)
case doCatchStatement(DoCatchStatement)
case externalCall(ExternalCall)

public var isEnding: Bool {
switch self {
Expand All @@ -34,6 +35,7 @@ public indirect enum Statement: ASTNode {
case .forStatement(let forStatement): return forStatement.sourceLocation
case .emitStatement(let emitStatement): return emitStatement.sourceLocation
case .doCatchStatement(let doCatchStatement): return doCatchStatement.sourceLocation
case .externalCall(let externalCall): return externalCall.sourceLocation
}
}
public var description: String {
Expand All @@ -45,6 +47,7 @@ public indirect enum Statement: ASTNode {
case .forStatement(let forStatement): return forStatement.description
case .emitStatement(let emitStatement): return emitStatement.description
case .doCatchStatement(let doCatchStatement): return doCatchStatement.description
case .externalCall(let externalCall): return externalCall.description
}
}
}
2 changes: 2 additions & 0 deletions Sources/IRGen/Function/IRStatement.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ struct IRStatement {
return IRForStatement(forStatement: forStatement).rendered(functionContext: functionContext)
case .doCatchStatement(let doCatchStatement):
return IRDoCatchStatement(doCatchStatement: doCatchStatement).rendered(functionContext: functionContext)
case .externalCall(let externalCall):
return IRExternalCallStatement(externalCall: externalCall).rendered(functionContext: functionContext)
}
}
}
Expand Down
5 changes: 4 additions & 1 deletion Sources/Parser/Parser+Statements.swift
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ extension Parser {
case .for:
let forStatement = try parseForStatement()
statement = .forStatement(forStatement)
case .call:
let externalCall = try parseExternalCall(upTo: statementEndIndex)
statement = .externalCall(externalCall)
case .if:
let ifStatement = try parseIfStatement()
statement = .ifStatement(ifStatement)
Expand All @@ -65,7 +68,7 @@ extension Parser {
statement = .doCatchStatement(doCatchStatement)
// Valid starting tokens for expressions
case .identifier, .punctuation(.ampersand), .punctuation(.openSquareBracket),
.punctuation(.openBracket), .self, .var, .let, .public, .visible, .mutating, .try, .call:
.punctuation(.openBracket), .self, .var, .let, .public, .visible, .mutating, .try:
let expression = try parseExpression(upTo: statementEndIndex)
statement = .expression(expression)
default:
Expand Down
10 changes: 6 additions & 4 deletions Sources/SemanticAnalyzer/SemanticAnalyzer+Declarations.swift
Original file line number Diff line number Diff line change
Expand Up @@ -268,22 +268,22 @@ extension SemanticAnalyzer {
let environment = passContext.environment!

// Check valid modifiers
if variableDeclaration.isMutating {
if variableDeclaration.isMutating {
if variableDeclaration.isConstant {
diagnostics.append(.mutatingConstant(variableDeclaration))
} else if variableDeclaration.isVariable {
diagnostics.append(.mutatingVariable(variableDeclaration))
}
}
}

if variableDeclaration.isPublic {
if variableDeclaration.isPublic {
if variableDeclaration.isConstant {
diagnostics.append(.publicLet(variableDeclaration))
}
if variableDeclaration.isVisible {
diagnostics.append(.publicAndVisible(variableDeclaration))
}
}
}

// Ensure that the type is declared.
if case .userDefinedType(let typeIdentifier) = variableDeclaration.type.rawType,
Expand Down Expand Up @@ -585,6 +585,8 @@ extension SemanticAnalyzer {
return true
case .emitStatement:
return false
case .externalCall:
fatalError()
}
return true
}
Expand Down
79 changes: 26 additions & 53 deletions Sources/SemanticAnalyzer/SemanticAnalyzer+Expressions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,40 +12,19 @@ extension SemanticAnalyzer {

public func process(binaryExpression: BinaryExpression,
passContext: ASTPassContext) -> ASTPassResult<BinaryExpression> {
var binaryExpression = binaryExpression
let environment = passContext.environment!
var diagnostics = [Diagnostic]()

switch binaryExpression.opToken {
case .dot:
// The identifier explicitly refers to a state property, such as in `self.foo`.
// We set its enclosing type to the type it is declared in.
if case .dot = binaryExpression.opToken {
let enclosingType = passContext.enclosingTypeIdentifier!
let lhsType = environment.type(of: binaryExpression.lhs,
enclosingType: enclosingType.name,
scopeContext: passContext.scopeContext!)
if case .identifier(let enumIdentifier) = binaryExpression.lhs,
environment.isEnumDeclared(enumIdentifier.name) {
binaryExpression.rhs = binaryExpression.rhs.assigningEnclosingType(type: enumIdentifier.name)
} else if lhsType == .selfType {
if let traitDeclarationContext = passContext.traitDeclarationContext {
binaryExpression.rhs =
binaryExpression.rhs.assigningEnclosingType(type: traitDeclarationContext.traitIdentifier.name)
} else {
diagnostics.append(.useOfSelfOutsideTrait(at: binaryExpression.lhs.sourceLocation))
}
} else {
binaryExpression.rhs = binaryExpression.rhs.assigningEnclosingType(type: lhsType.name)
}
case .equal:
// Check if `call?` assignment
if case .externalCall(let externalCall) = binaryExpression.rhs,
externalCall.mode == .returnsGracefullyOptional {
diagnostics.append(.externalCallOptionalAssignmentNotImplemented(binaryExpression))
} else if lhsType == .selfType, passContext.traitDeclarationContext == nil {
diagnostics.append(.useOfSelfOutsideTrait(at: binaryExpression.lhs.sourceLocation))
}

default:
break
}

return ASTPassResult(element: binaryExpression, diagnostics: diagnostics, passContext: passContext)
Expand Down Expand Up @@ -104,33 +83,26 @@ extension SemanticAnalyzer {
return ASTPassResult(element: attemptExpression, diagnostics: diagnostics, passContext: passContext)
}

public func process(arrayLiteral: ArrayLiteral, passContext: ASTPassContext) -> ASTPassResult<AST.ArrayLiteral> {
return ASTPassResult(element: arrayLiteral, diagnostics: [], passContext: passContext)
}

public func process(rangeExpression: AST.RangeExpression,
passContext: ASTPassContext) -> ASTPassResult<AST.RangeExpression> {
public func process(functionCall: FunctionCall, passContext: ASTPassContext) -> ASTPassResult<FunctionCall> {
let environment = passContext.environment!
var diagnostics = [Diagnostic]()

if case .literal(let startToken) = rangeExpression.initial,
case .literal(let endToken) = rangeExpression.bound {
if startToken.kind == endToken.kind, rangeExpression.op.kind == .punctuation(.halfOpenRange) {
diagnostics.append(.emptyRange(rangeExpression))
}
} else {
diagnostics.append(.invalidRangeDeclaration(rangeExpression.initial))
if environment.isInitializerCall(functionCall),
!passContext.inAssignment,
!passContext.isPropertyDefaultAssignment,
functionCall.arguments.isEmpty {
diagnostics.append(.noReceiverForStructInitializer(functionCall))
}

return ASTPassResult(element: rangeExpression, diagnostics: diagnostics, passContext: passContext)
return ASTPassResult(element: functionCall, diagnostics: diagnostics, passContext: passContext)
}

public func process(externalCall: ExternalCall, passContext: ASTPassContext) -> ASTPassResult<ExternalCall> {
var diagnostics = [Diagnostic]()

var parametersGiven: [String: Bool] = [
"value": false,
"gas": false
]
var diagnostics = [Diagnostic]()

// ensure only one instance of value and gas hyper-parameters
for parameter in externalCall.hyperParameters {
Expand All @@ -149,26 +121,27 @@ extension SemanticAnalyzer {
}
}

// Ensure `call` is only used inside do-catch block
if externalCall.mode == .normal && passContext.doBlockNestingCount <= 0 {
diagnostics.append(.normalExternalCallOutsideDoCatch(externalCall))
}

return ASTPassResult(element: externalCall, diagnostics: diagnostics, passContext: passContext)
}

public func process(functionCall: FunctionCall, passContext: ASTPassContext) -> ASTPassResult<FunctionCall> {
let environment = passContext.environment!
public func process(arrayLiteral: ArrayLiteral, passContext: ASTPassContext) -> ASTPassResult<AST.ArrayLiteral> {
return ASTPassResult(element: arrayLiteral, diagnostics: [], passContext: passContext)
}

public func process(rangeExpression: AST.RangeExpression,
passContext: ASTPassContext) -> ASTPassResult<AST.RangeExpression> {
var diagnostics = [Diagnostic]()

if environment.isInitializerCall(functionCall),
!passContext.inAssignment,
!passContext.isPropertyDefaultAssignment,
functionCall.arguments.isEmpty {
diagnostics.append(.noReceiverForStructInitializer(functionCall))
if case .literal(let startToken) = rangeExpression.initial,
case .literal(let endToken) = rangeExpression.bound {
if startToken.kind == endToken.kind, rangeExpression.op.kind == .punctuation(.halfOpenRange) {
diagnostics.append(.emptyRange(rangeExpression))
}
} else {
diagnostics.append(.invalidRangeDeclaration(rangeExpression.initial))
}

return ASTPassResult(element: functionCall, diagnostics: diagnostics, passContext: passContext)
return ASTPassResult(element: rangeExpression, diagnostics: diagnostics, passContext: passContext)
}

public func postProcess(functionCall: FunctionCall, passContext: ASTPassContext) -> ASTPassResult<FunctionCall> {
Expand Down
13 changes: 0 additions & 13 deletions Sources/SemanticAnalyzer/SemanticError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -504,19 +504,6 @@ extension Diagnostic {
static func renderGroup(_ states: [TypeState]) -> String {
return "\(states.map({ $0.name }).joined(separator: ", "))"
}

// EXTERNAL CALL ERRORS //
static func normalExternalCallOutsideDoCatch(_ externalCall: ExternalCall) -> Diagnostic {
return Diagnostic(severity: .error, sourceLocation: externalCall.sourceLocation,
message: "Cannot use 'call' outside do-catch block")
}

// As there are currently no optional types, we cannot assign to an optional return type.
static func externalCallOptionalAssignmentNotImplemented(_ variableDeclarationExpr: BinaryExpression) -> Diagnostic {
return Diagnostic(severity: .error, sourceLocation: variableDeclarationExpr.sourceLocation,
message: "Assignment to the optional result of 'call?' is not yet implemented")
}

}

// MARK: Warnings
Expand Down
14 changes: 0 additions & 14 deletions Sources/TypeChecker/TypeChecker+Expressions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -173,20 +173,6 @@ extension TypeChecker {
return ASTPassResult(element: functionCall, diagnostics: diagnostics, passContext: passContext)
}

public func process(externalCall: ExternalCall, passContext: ASTPassContext) -> ASTPassResult<ExternalCall> {
var diagnostics = [Diagnostic]()
let environment = passContext.environment!
let enclosingType = passContext.enclosingTypeIdentifier!.name

if externalCall.mode == .returnsGracefullyOptional && environment.type(of: externalCall.functionCall.rhs,
enclosingType: enclosingType,
scopeContext: passContext.scopeContext!) == .basicType(.void) {
diagnostics.append(.optionalExternalCallWithoutReturnType(externalCall: externalCall))
}

return ASTPassResult(element: externalCall, diagnostics: diagnostics, passContext: passContext)
}

public func process(subscriptExpression: SubscriptExpression,
passContext: ASTPassContext) -> ASTPassResult<SubscriptExpression> {
var diagnostics = [Diagnostic]()
Expand Down
5 changes: 0 additions & 5 deletions Sources/TypeChecker/TypeError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,6 @@ extension Diagnostic {
message: "Cannot convert expression of type '\(actualType.name)' to expected return type '\(expectedType.name)'")
}

static func optionalExternalCallWithoutReturnType(externalCall: ExternalCall) -> Diagnostic {
return Diagnostic(severity: .error, sourceLocation: externalCall.sourceLocation,
message: "Cannot use 'call?' with external function that has no return type")
}

static func invalidState(falseState: Expression, contract: RawTypeIdentifier) -> Diagnostic {
return Diagnostic(severity: .error, sourceLocation: falseState.sourceLocation,
message: "State not defined for contract '\(contract)'")
Expand Down
12 changes: 6 additions & 6 deletions Tests/Integration/ParserTests/external_traits.flint
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ Contract :: (any) {
let addr: Address = 0x0000000000000000000000000000000000000000
let contr: Test = Test(address: addr)

// CHECK-AST: Expression (
// CHECK-AST: Statement (
// CHECK-AST: ExternalCall (
// CHECK-AST: BinaryExpression (
// CHECK-AST: Expression (
Expand All @@ -123,7 +123,7 @@ Contract :: (any) {
// CHECK-AST: )
call contr.f()

// CHECK-AST: Expression (
// CHECK-AST: Statement (
// CHECK-AST: ExternalCall (
// CHECK-AST: FunctionArgument (
// CHECK-AST: Identifier (
Expand Down Expand Up @@ -153,7 +153,7 @@ Contract :: (any) {
// CHECK-AST: )
call(wei: 100) contr.f()

// CHECK-AST: Expression (
// CHECK-AST: Statement (
// CHECK-AST: ExternalCall (
// CHECK-AST: FunctionArgument (
// CHECK-AST: Identifier (
Expand Down Expand Up @@ -185,7 +185,7 @@ Contract :: (any) {
// CHECK-AST: )
call(wei: 100)! contr.f()

// CHECK-AST: Expression (
// CHECK-AST: Statement (
// CHECK-AST: ExternalCall (
// CHECK-AST: Forced execution (
// CHECK-AST: )
Expand All @@ -209,7 +209,7 @@ Contract :: (any) {
// CHECK-AST: )
call! contr.f()

// CHECK-AST: Expression (
// CHECK-AST: Statement (
// CHECK-AST: ExternalCall (
// CHECK-AST: Returns optional (
// CHECK-AST: )
Expand All @@ -233,7 +233,7 @@ Contract :: (any) {
// CHECK-AST: )
call? contr.f()

// CHECK-AST: Expression (
// CHECK-AST: Statement (
// CHECK-AST: ExternalCall (
// CHECK-AST: Forced execution (
// CHECK-AST: )
Expand Down
Loading

0 comments on commit d757f29

Please sign in to comment.