Skip to content

Commit da5f04a

Browse files
committed
Fix compiling emit-statements in inherited conditions
1 parent de0f132 commit da5f04a

File tree

3 files changed

+410
-10
lines changed

3 files changed

+410
-10
lines changed

bbq/compiler/compiler.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1013,8 +1013,8 @@ func (c *Compiler[_, _]) VisitEmitStatement(statement *ast.EmitStatement) (_ str
10131013
c.withConditionExtendedElaboration(
10141014
statement,
10151015
func() {
1016-
eventType := c.ExtendedElaboration.EmitStatementEventType(statement)
10171016
c.compileExpression(statement.InvocationExpression)
1017+
eventType := c.ExtendedElaboration.EmitStatementEventType(statement)
10181018
typeIndex := c.getOrAddType(eventType)
10191019
c.codeGen.Emit(opcode.InstructionEmitEvent{
10201020
Type: typeIndex,

bbq/compiler/desugar.go

Lines changed: 114 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -349,7 +349,7 @@ func (d *Desugar) desugarPreConditions(
349349
conditions = funcBlock.PreConditions
350350

351351
for _, condition := range conditions.Conditions {
352-
desugaredCondition := d.desugarCondition(condition)
352+
desugaredCondition := d.desugarCondition(condition, false)
353353
desugaredConditions = append(desugaredConditions, desugaredCondition)
354354
}
355355
}
@@ -383,7 +383,7 @@ func (d *Desugar) desugarPostConditions(
383383
beforeStatements = postConditionsRewrite.BeforeStatements
384384

385385
for _, condition := range conditionsList {
386-
desugaredCondition := d.desugarCondition(condition)
386+
desugaredCondition := d.desugarCondition(condition, false)
387387
desugaredConditions = append(desugaredConditions, desugaredCondition)
388388
}
389389
}
@@ -438,7 +438,7 @@ func (d *Desugar) desugarInheritedCondition(condition ast.Condition, inheritedFu
438438
prevElaboration := d.elaboration
439439
d.elaboration = inheritedFunc.elaboration
440440

441-
desugaredCondition := d.desugarCondition(condition)
441+
desugaredCondition := d.desugarCondition(condition, true)
442442
d.elaboration = prevElaboration
443443

444444
// Elaboration to be used by the condition must be set in the current elaboration.
@@ -464,7 +464,7 @@ var panicFuncInvocationTypes = sema.InvocationExpressionTypes{
464464
},
465465
}
466466

467-
func (d *Desugar) desugarCondition(condition ast.Condition) ast.Statement {
467+
func (d *Desugar) desugarCondition(condition ast.Condition, isInherited bool) ast.Statement {
468468
switch condition := condition.(type) {
469469
case *ast.TestCondition:
470470

@@ -533,12 +533,115 @@ func (d *Desugar) desugarCondition(condition ast.Condition) ast.Statement {
533533
return ifStmt
534534
case *ast.EmitCondition:
535535
emitStmt := (*ast.EmitStatement)(condition)
536-
return emitStmt
536+
537+
if !isInherited {
538+
return emitStmt
539+
}
540+
541+
// If the condition is inherited, then re-write
542+
// the emit statement to be type-qualified.
543+
// i.e: `emit Event()` will be re-written as `emit Contract.Event()`.
544+
// Otherwise, the compiler can't find the symbol.
545+
546+
eventType := d.elaboration.EmitStatementEventType(emitStmt)
547+
548+
// Get the contract in which the event was declared.
549+
// This is guaranteed, since events can only be declared in contracts.
550+
declaredContract := declaredContract(eventType).(sema.CompositeKindedType)
551+
552+
eventConstructorInvocation := emitStmt.InvocationExpression
553+
554+
// If the event constructor is already type-qualified, then no need to change anything.
555+
if _, ok := eventConstructorInvocation.InvokedExpression.(*ast.MemberExpression); ok {
556+
return emitStmt
557+
}
558+
559+
// Otherwise, make it type-qualified
560+
invocationTypes := d.elaboration.InvocationExpressionTypes(eventConstructorInvocation)
561+
562+
pos := eventConstructorInvocation.StartPosition()
563+
564+
memberExpression := ast.NewMemberExpression(
565+
d.memoryGauge,
566+
ast.NewIdentifierExpression(
567+
d.memoryGauge,
568+
ast.NewIdentifier(
569+
d.memoryGauge,
570+
declaredContract.GetIdentifier(),
571+
pos,
572+
),
573+
),
574+
false,
575+
pos,
576+
ast.NewIdentifier(
577+
d.memoryGauge,
578+
eventType.Identifier,
579+
pos,
580+
),
581+
)
582+
583+
newEventConstructorInvocation := ast.NewInvocationExpression(
584+
d.memoryGauge,
585+
memberExpression,
586+
eventConstructorInvocation.TypeArguments,
587+
eventConstructorInvocation.Arguments,
588+
eventConstructorInvocation.ArgumentsStartPos,
589+
eventConstructorInvocation.EndPos,
590+
)
591+
592+
newEmitStmt := ast.NewEmitStatement(
593+
d.memoryGauge,
594+
newEventConstructorInvocation,
595+
emitStmt.StartPos,
596+
)
597+
598+
//Inject a static import so the compiler can link the functions.
599+
d.addImport(eventType.Location)
600+
601+
d.elaboration.SetInvocationExpressionTypes(newEventConstructorInvocation, invocationTypes)
602+
d.elaboration.SetEmitStatementEventType(newEmitStmt, eventType)
603+
604+
// TODO: Is there a way to get the type for the constructor
605+
// from the elaboration, rather than manually constructing it here?
606+
eventConstructorFuncType := sema.NewSimpleFunctionType(
607+
sema.FunctionPurityImpure,
608+
// Parameters are not needed, since they are not used in the compiler
609+
nil,
610+
sema.NewTypeAnnotation(eventType),
611+
)
612+
eventConstructorFuncType.IsConstructor = true
613+
614+
memberAccessInfo := sema.MemberAccessInfo{
615+
AccessedType: declaredContract,
616+
ResultingType: eventType,
617+
Member: sema.NewPublicFunctionMember(
618+
d.memoryGauge,
619+
declaredContract,
620+
eventType.Identifier,
621+
eventConstructorFuncType,
622+
"",
623+
),
624+
IsOptional: false,
625+
ReturnReference: false,
626+
}
627+
628+
d.elaboration.SetMemberExpressionMemberAccessInfo(memberExpression, memberAccessInfo)
629+
630+
return newEmitStmt
537631
default:
538632
panic(errors.NewUnreachableError())
539633
}
540634
}
541635

636+
func declaredContract(containedType sema.ContainedType) sema.Type {
637+
containerType := containedType.GetContainerType()
638+
if containerType == nil {
639+
return containedType
640+
}
641+
642+
return declaredContract(containerType.(sema.ContainedType))
643+
}
644+
542645
func (d *Desugar) VisitSpecialFunctionDeclaration(declaration *ast.SpecialFunctionDeclaration) ast.Declaration {
543646
desugaredDecl := d.desugarDeclaration(declaration.FunctionDeclaration).(*ast.FunctionDeclaration)
544647
if desugaredDecl == declaration.FunctionDeclaration {
@@ -921,8 +1024,13 @@ func (d *Desugar) interfaceDelegationMethodCall(
9211024
// Given these invocations are treated as static calls,
9221025
// we need to inject a static import as well, so the
9231026
// compiler can link these functions.
1027+
d.addImport(interfaceType.Location)
1028+
1029+
return invocation
1030+
}
9241031

925-
interfaceLocation, isAddressLocation := interfaceType.Location.(common.AddressLocation)
1032+
func (d *Desugar) addImport(location common.Location) {
1033+
interfaceLocation, isAddressLocation := location.(common.AddressLocation)
9261034
if isAddressLocation {
9271035
if _, exists := d.importsSet[interfaceLocation]; !exists {
9281036
d.newImports = append(
@@ -940,8 +1048,6 @@ func (d *Desugar) interfaceDelegationMethodCall(
9401048
d.importsSet[interfaceLocation] = struct{}{}
9411049
}
9421050
}
943-
944-
return invocation
9451051
}
9461052

9471053
func (d *Desugar) VisitInterfaceDeclaration(declaration *ast.InterfaceDeclaration) ast.Declaration {

0 commit comments

Comments
 (0)