Skip to content

Commit

Permalink
Merge pull request #2982 from darkdrag00nv2/copy_from_reference
Browse files Browse the repository at this point in the history
  • Loading branch information
turbolent authored Jan 10, 2024
2 parents 03ecaca + 129d7c5 commit 24f277e
Show file tree
Hide file tree
Showing 9 changed files with 1,704 additions and 41 deletions.
19 changes: 17 additions & 2 deletions runtime/interpreter/interpreter_expression.go
Original file line number Diff line number Diff line change
Expand Up @@ -688,10 +688,25 @@ func (interpreter *Interpreter) VisitUnaryExpression(expression *ast.UnaryExpres
if !ok {
panic(errors.NewUnreachableError())
}
return integerValue.Negate(interpreter, LocationRange{
return integerValue.Negate(
interpreter,
LocationRange{
Location: interpreter.Location,
HasPosition: expression,
},
)

case ast.OperationMul:
referenceValue, ok := value.(ReferenceValue)
if !ok {
panic(errors.NewUnreachableError())
}
locationRange := LocationRange{
Location: interpreter.Location,
HasPosition: expression,
})
}

return DereferenceValue(interpreter, locationRange, referenceValue)

case ast.OperationMove:
interpreter.invalidateResource(value)
Expand Down
16 changes: 16 additions & 0 deletions runtime/interpreter/value.go
Original file line number Diff line number Diff line change
Expand Up @@ -19752,6 +19752,22 @@ type ReferenceValue interface {
ReferencedValue(interpreter *Interpreter, locationRange LocationRange, errorOnFailedDereference bool) *Value
}

func DereferenceValue(
inter *Interpreter,
locationRange LocationRange,
referenceValue ReferenceValue,
) Value {
referencedValue := referenceValue.ReferencedValue(inter, locationRange, true)
return (*referencedValue).Transfer(
inter,
locationRange,
atree.Address{},
false,
nil,
nil,
)
}

// StorageReferenceValue
type StorageReferenceValue struct {
BorrowedType sema.Type
Expand Down
6 changes: 6 additions & 0 deletions runtime/parser/expression.go
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,12 @@ func init() {
operation: ast.OperationMove,
})

defineExpr(unaryExpr{
tokenType: lexer.TokenStar,
bindingPower: exprLeftBindingPowerUnaryPrefix,
operation: ast.OperationMul,
})

defineExpr(postfixExpr{
tokenType: lexer.TokenExclamationMark,
bindingPower: exprLeftBindingPowerUnaryPostfix,
Expand Down
148 changes: 120 additions & 28 deletions runtime/parser/expression_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2201,6 +2201,43 @@ func TestParseBlockComment(t *testing.T) {
})
}

func TestParseMulInfixExpression(t *testing.T) {

t.Parallel()

result, errs := testParseExpression(" 1 ** 2")
require.Empty(t, errs)

utils.AssertEqualWithDiff(t,
&ast.BinaryExpression{
Operation: ast.OperationMul,
Left: &ast.IntegerExpression{
PositiveLiteral: []byte("1"),
Value: big.NewInt(1),
Base: 10,
Range: ast.Range{
StartPos: ast.Position{Line: 1, Column: 1, Offset: 1},
EndPos: ast.Position{Line: 1, Column: 1, Offset: 1},
},
},
Right: &ast.UnaryExpression{
Operation: ast.OperationMul,
Expression: &ast.IntegerExpression{
PositiveLiteral: []byte("2"),
Value: big.NewInt(2),
Base: 10,
Range: ast.Range{
StartPos: ast.Position{Line: 1, Column: 6, Offset: 6},
EndPos: ast.Position{Line: 1, Column: 6, Offset: 6},
},
},
StartPos: ast.Position{Line: 1, Column: 4, Offset: 4},
},
},
result,
)
}

func BenchmarkParseInfix(b *testing.B) {

for i := 0; i < b.N; i++ {
Expand Down Expand Up @@ -4949,40 +4986,95 @@ func TestParseUnaryExpression(t *testing.T) {

t.Parallel()

const code = `
let foo = -boo
`
result, errs := testParseProgram(code)
require.Empty(t, errs)
t.Run("minus", func(t *testing.T) {

utils.AssertEqualWithDiff(t,
[]ast.Declaration{
&ast.VariableDeclaration{
Access: ast.AccessNotSpecified,
IsConstant: true,
Identifier: ast.Identifier{
Identifier: "foo",
Pos: ast.Position{Offset: 10, Line: 2, Column: 9},
t.Parallel()

const code = ` - boo`

result, errs := testParseExpression(code)
require.Empty(t, errs)

utils.AssertEqualWithDiff(t,
&ast.UnaryExpression{
Operation: ast.OperationMinus,
Expression: &ast.IdentifierExpression{
Identifier: ast.Identifier{
Identifier: "boo",
Pos: ast.Position{Offset: 3, Line: 1, Column: 3},
},
},
Transfer: &ast.Transfer{
Operation: ast.TransferOperationCopy,
Pos: ast.Position{Offset: 14, Line: 2, Column: 13},
StartPos: ast.Position{Offset: 1, Line: 1, Column: 1},
},
result,
)
})

t.Run("negate", func(t *testing.T) {

t.Parallel()

const code = ` ! boo`

result, errs := testParseExpression(code)
require.Empty(t, errs)

utils.AssertEqualWithDiff(t,
&ast.UnaryExpression{
Operation: ast.OperationNegate,
Expression: &ast.IdentifierExpression{
Identifier: ast.Identifier{
Identifier: "boo",
Pos: ast.Position{Offset: 3, Line: 1, Column: 3},
},
},
Value: &ast.UnaryExpression{
Operation: ast.OperationMinus,
Expression: &ast.IdentifierExpression{
Identifier: ast.Identifier{
Identifier: "boo",
Pos: ast.Position{Offset: 17, Line: 2, Column: 16},
},
StartPos: ast.Position{Offset: 1, Line: 1, Column: 1},
},
result,
)
})

t.Run("star", func(t *testing.T) {

t.Parallel()

const code = ` * boo`

result, errs := testParseExpression(code)
require.Empty(t, errs)

utils.AssertEqualWithDiff(t,
&ast.UnaryExpression{
Operation: ast.OperationMul,
Expression: &ast.IdentifierExpression{
Identifier: ast.Identifier{
Identifier: "boo",
Pos: ast.Position{Offset: 3, Line: 1, Column: 3},
},
StartPos: ast.Position{Offset: 16, Line: 2, Column: 15},
},
StartPos: ast.Position{Offset: 6, Line: 2, Column: 5},
StartPos: ast.Position{Offset: 1, Line: 1, Column: 1},
},
},
result.Declarations(),
)
result,
)
})

t.Run("invalid", func(t *testing.T) {

t.Parallel()

const code = ` % boo`

_, errs := testParseExpression(code)
utils.AssertEqualWithDiff(t,
[]error{
&SyntaxError{
Message: "unexpected token in expression: '%'",
Pos: ast.Position{Line: 1, Column: 2, Offset: 2},
},
},
errs,
)
})
}

func TestParseOrExpression(t *testing.T) {
Expand Down
38 changes: 38 additions & 0 deletions runtime/sema/check_unary_expression.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,44 @@ func (checker *Checker) VisitUnaryExpression(expression *ast.UnaryExpression) Ty
case ast.OperationMinus:
return checkExpectedType(valueType, SignedNumberType)

case ast.OperationMul:
referenceType, ok := valueType.(*ReferenceType)
if !ok {
if !valueType.IsInvalidType() {
checker.report(
&InvalidUnaryOperandError{
Operation: expression.Operation,
ExpectedTypeDescription: "reference type",
ActualType: valueType,
Range: ast.NewRangeFromPositioned(
checker.memoryGauge,
expression.Expression,
),
},
)
return InvalidType
}
}

innerType := referenceType.Type

// Allow primitives or containers of primitives.
if !IsPrimitiveOrContainerOfPrimitive(innerType) {
checker.report(
&InvalidUnaryOperandError{
Operation: expression.Operation,
ExpectedTypeDescription: "primitive or container of primitives",
ActualType: innerType,
Range: ast.NewRangeFromPositioned(
checker.memoryGauge,
expression.Expression,
),
},
)
}

return innerType

case ast.OperationMove:
if !valueType.IsInvalidType() &&
!valueType.IsResourceType() {
Expand Down
32 changes: 21 additions & 11 deletions runtime/sema/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -589,8 +589,9 @@ func (e *IncorrectArgumentLabelError) SuggestFixes(code string) []errors.Suggest
// InvalidUnaryOperandError

type InvalidUnaryOperandError struct {
ExpectedType Type
ActualType Type
ExpectedType Type
ExpectedTypeDescription string
ActualType Type
ast.Range
Operation ast.Operation
}
Expand All @@ -611,16 +612,25 @@ func (e *InvalidUnaryOperandError) Error() string {
}

func (e *InvalidUnaryOperandError) SecondaryError() string {
expected, actual := ErrorMessageExpectedActualTypes(
e.ExpectedType,
e.ActualType,
)
expectedType := e.ExpectedType
if expectedType != nil {
expected, actual := ErrorMessageExpectedActualTypes(
e.ExpectedType,
e.ActualType,
)

return fmt.Sprintf(
"expected `%s`, got `%s`",
expected,
actual,
)
return fmt.Sprintf(
"expected `%s`, got `%s`",
expected,
actual,
)
} else {
return fmt.Sprintf(
"expected %s, got `%s`",
e.ExpectedTypeDescription,
e.ActualType.QualifiedString(),
)
}
}

// InvalidBinaryOperandError
Expand Down
16 changes: 16 additions & 0 deletions runtime/sema/type.go
Original file line number Diff line number Diff line change
Expand Up @@ -6448,6 +6448,22 @@ func (t *AddressType) initializeMemberResolvers() {
})
}

func IsPrimitiveOrContainerOfPrimitive(ty Type) bool {
switch ty := ty.(type) {
case *VariableSizedType:
return IsPrimitiveOrContainerOfPrimitive(ty.Type)

case *ConstantSizedType:
return IsPrimitiveOrContainerOfPrimitive(ty.Type)

case *DictionaryType:
return IsPrimitiveOrContainerOfPrimitive(ty.ValueType)

default:
return ty.IsPrimitiveType()
}
}

// IsSubType determines if the given subtype is a subtype
// of the given supertype.
//
Expand Down
Loading

0 comments on commit 24f277e

Please sign in to comment.