Skip to content

Commit

Permalink
Merge pull request #3493 from onflow/supun/fix-supertype-inference
Browse files Browse the repository at this point in the history
  • Loading branch information
turbolent authored Jul 25, 2024
2 parents d39a165 + 5e772b0 commit 0276b47
Show file tree
Hide file tree
Showing 8 changed files with 67 additions and 23 deletions.
10 changes: 1 addition & 9 deletions runtime/sema/check_array_expression.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,16 +80,8 @@ func (checker *Checker) VisitArrayExpression(arrayExpression *ast.ArrayExpressio
if elementType == nil {
// Contextually expected type is not available.
// Therefore, find the least common supertype of the elements.
elementType = LeastCommonSuperType(argumentTypes...)

elementType = checker.leastCommonSuperType(arrayExpression, argumentTypes...)
if elementType == InvalidType {
checker.report(
&TypeAnnotationRequiredError{
Cause: "cannot infer type from array literal:",
Pos: arrayExpression.StartPos,
},
)

return InvalidType
}

Expand Down
2 changes: 1 addition & 1 deletion runtime/sema/check_binary_expression.go
Original file line number Diff line number Diff line change
Expand Up @@ -468,5 +468,5 @@ func (checker *Checker) checkBinaryExpressionNilCoalescing(
}
}

return LeastCommonSuperType(leftOptional.Type, rightType)
return checker.leastCommonSuperType(expression, leftOptional.Type, rightType)
}
2 changes: 1 addition & 1 deletion runtime/sema/check_conditional.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ func (checker *Checker) VisitConditionalExpression(expression *ast.ConditionalEx
return thenType
}

return LeastCommonSuperType(thenType, elseType)
return checker.leastCommonSuperType(expression, thenType, elseType)
}

// checkConditionalBranches checks two conditional branches.
Expand Down
17 changes: 6 additions & 11 deletions runtime/sema/check_dictionary_expression.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,18 +69,13 @@ func (checker *Checker) VisitDictionaryExpression(expression *ast.DictionaryExpr
if keyType == nil && valueType == nil {
// Contextually expected type is not available.
// Therefore, find the least common supertype of the keys and values.
keyType = LeastCommonSuperType(keyTypes...)
valueType = LeastCommonSuperType(valueTypes...)

if keyType == InvalidType ||
valueType == InvalidType {
checker.report(
&TypeAnnotationRequiredError{
Cause: "cannot infer type from dictionary literal:",
Pos: expression.StartPos,
},
)
keyType = checker.leastCommonSuperType(expression, keyTypes...)
if keyType == InvalidType {
return InvalidType
}

valueType = checker.leastCommonSuperType(expression, valueTypes...)
if valueType == InvalidType {
return InvalidType
}
}
Expand Down
15 changes: 15 additions & 0 deletions runtime/sema/checker.go
Original file line number Diff line number Diff line change
Expand Up @@ -2766,3 +2766,18 @@ func (checker *Checker) checkNativeModifier(isNative bool, position ast.HasPosit
)
}
}

func (checker *Checker) leastCommonSuperType(pos ast.HasPosition, types ...Type) Type {
elementType := LeastCommonSuperType(types...)

if elementType == InvalidType {
checker.report(
&TypeAnnotationRequiredError{
Cause: "cannot infer type:",
Pos: pos.StartPosition(),
},
)
}

return elementType
}
5 changes: 5 additions & 0 deletions runtime/sema/type_tags.go
Original file line number Diff line number Diff line change
Expand Up @@ -997,6 +997,11 @@ func commonSuperTypeOfHeterogeneousTypes(types []Type) Type {
var hasStructs, hasResources, allHashableStructs bool
allHashableStructs = true
for _, typ := range types {
// Ignore 'Never' type as it doesn't affect the supertype.
if typ == NeverType {
continue
}

isResource := typ.IsResourceType()
hasResources = hasResources || isResource
hasStructs = hasStructs || !isResource
Expand Down
3 changes: 2 additions & 1 deletion runtime/tests/checker/conditional_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,10 @@ func TestCheckInvalidConditionalExpressionElse(t *testing.T) {
let x = true ? 2 : y
`)

errs := RequireCheckerErrors(t, err, 1)
errs := RequireCheckerErrors(t, err, 2)

assert.IsType(t, &sema.NotDeclaredError{}, errs[0])
assert.IsType(t, &sema.TypeAnnotationRequiredError{}, errs[1])

xType := RequireGlobalValue(t, checker.Elaboration, "x")
assert.Equal(t, sema.InvalidType, xType)
Expand Down
36 changes: 36 additions & 0 deletions runtime/tests/interpreter/interpreter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11684,6 +11684,42 @@ func TestInterpretNilCoalesceReference(t *testing.T) {
)
}

func TestInterpretNilCoalesceAnyResourceAndPanic(t *testing.T) {

t.Parallel()

baseValueActivation := sema.NewVariableActivation(sema.BaseValueActivation)
baseValueActivation.DeclareValue(stdlib.PanicFunction)

baseActivation := activations.NewActivation(nil, interpreter.BaseActivation)
interpreter.Declare(baseActivation, stdlib.PanicFunction)

_, err := parseCheckAndInterpretWithOptions(t,
`
resource R {}
fun f(): @AnyResource? {
return <-create R()
}
let y <- f() ?? panic("no R")
`,
ParseCheckAndInterpretOptions{
CheckerConfig: &sema.Config{
BaseValueActivationHandler: func(_ common.Location) *sema.VariableActivation {
return baseValueActivation
},
},
Config: &interpreter.Config{
BaseActivationHandler: func(_ common.Location) *interpreter.VariableActivation {
return baseActivation
},
},
},
)
require.NoError(t, err)
}

func TestInterpretDictionaryDuplicateKey(t *testing.T) {

t.Parallel()
Expand Down

0 comments on commit 0276b47

Please sign in to comment.