Skip to content

Commit 95b8688

Browse files
committed
add Bool.toString()
1 parent bc9c8c2 commit 95b8688

9 files changed

Lines changed: 213 additions & 12 deletions

File tree

bbq/vm/test/vm_test.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9512,6 +9512,43 @@ func TestGetAuthAccount(t *testing.T) {
95129512
})
95139513
}
95149514

9515+
func TestBoolToString(t *testing.T) {
9516+
9517+
t.Parallel()
9518+
9519+
t.Run("true", func(t *testing.T) {
9520+
t.Parallel()
9521+
9522+
result, err := CompileAndInvoke(t,
9523+
`
9524+
fun test(): String {
9525+
let x: Bool = true
9526+
return x.toString()
9527+
}
9528+
`,
9529+
"test",
9530+
)
9531+
require.NoError(t, err)
9532+
require.Equal(t, interpreter.NewUnmeteredStringValue("true"), result)
9533+
})
9534+
9535+
t.Run("false", func(t *testing.T) {
9536+
t.Parallel()
9537+
9538+
result, err := CompileAndInvoke(t,
9539+
`
9540+
fun test(): String {
9541+
let x: Bool = false
9542+
return x.toString()
9543+
}
9544+
`,
9545+
"test",
9546+
)
9547+
require.NoError(t, err)
9548+
require.Equal(t, interpreter.NewUnmeteredStringValue("false"), result)
9549+
})
9550+
}
9551+
95159552
func TestStringTemplate(t *testing.T) {
95169553

95179554
t.Parallel()
@@ -9550,6 +9587,23 @@ func TestStringTemplate(t *testing.T) {
95509587
require.NoError(t, err)
95519588
require.Equal(t, interpreter.NewUnmeteredStringValue("A + B = 4"), result)
95529589
})
9590+
9591+
t.Run("bool", func(t *testing.T) {
9592+
t.Parallel()
9593+
9594+
result, err := CompileAndInvoke(t,
9595+
`
9596+
fun test(): String {
9597+
let x = true
9598+
let y = false
9599+
return "\(x) and \(y)"
9600+
}
9601+
`,
9602+
"test",
9603+
)
9604+
require.NoError(t, err)
9605+
require.Equal(t, interpreter.NewUnmeteredStringValue("true and false"), result)
9606+
})
95539607
}
95549608

95559609
type assumeValidPublicKeyValidator struct{}

bbq/vm/value_bool.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
* Cadence - The resource-oriented smart contract programming language
3+
*
4+
* Copyright Flow Foundation
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
package vm
20+
21+
import (
22+
"github.com/onflow/cadence/bbq/commons"
23+
"github.com/onflow/cadence/interpreter"
24+
"github.com/onflow/cadence/sema"
25+
)
26+
27+
// members
28+
29+
func init() {
30+
31+
typeName := commons.TypeQualifier(sema.BoolType)
32+
33+
registerBuiltinTypeBoundFunction(
34+
typeName,
35+
NewNativeFunctionValue(
36+
sema.ToStringFunctionName,
37+
sema.ToStringFunctionType,
38+
interpreter.NativeBoolValueToStringFunction,
39+
),
40+
)
41+
}

bbq/vm/value_path.go

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,7 @@ import (
2828

2929
func init() {
3030

31-
for _, pathType := range []sema.Type{
32-
sema.PathType,
33-
sema.StoragePathType,
34-
sema.CapabilityPathType,
35-
sema.PublicPathType,
36-
sema.PrivatePathType,
37-
} {
31+
for _, pathType := range sema.AllPathTypes {
3832
typeName := commons.TypeQualifier(pathType)
3933

4034
registerBuiltinTypeBoundFunction(

interpreter/builtinfunctions_test.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,40 @@ func TestInterpretToString(t *testing.T) {
8282
)
8383
})
8484

85+
t.Run("Bool, true", func(t *testing.T) {
86+
87+
t.Parallel()
88+
89+
inter := parseCheckAndPrepare(t, `
90+
let x: Bool = true
91+
let y = x.toString()
92+
`)
93+
94+
AssertValuesEqual(
95+
t,
96+
inter,
97+
interpreter.NewUnmeteredStringValue("true"),
98+
inter.GetGlobal("y"),
99+
)
100+
})
101+
102+
t.Run("Bool, false", func(t *testing.T) {
103+
104+
t.Parallel()
105+
106+
inter := parseCheckAndPrepare(t, `
107+
let x: Bool = false
108+
let y = x.toString()
109+
`)
110+
111+
AssertValuesEqual(
112+
t,
113+
inter,
114+
interpreter.NewUnmeteredStringValue("false"),
115+
inter.GetGlobal("y"),
116+
)
117+
})
118+
85119
for _, ty := range sema.AllFixedPointTypes {
86120

87121
t.Run(ty.String(), func(t *testing.T) {

interpreter/value_bool.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ type BoolValue values.BoolValue
3434
var _ Value = BoolValue(false)
3535
var _ EquatableValue = BoolValue(false)
3636
var _ HashableValue = BoolValue(false)
37+
var _ MemberAccessibleValue = BoolValue(false)
3738

3839
const TrueValue = BoolValue(true)
3940
const FalseValue = BoolValue(false)
@@ -191,3 +192,55 @@ func (v BoolValue) Clone(_ ValueCloneContext) Value {
191192
func (BoolValue) DeepRemove(_ ValueRemoveContext, _ bool) {
192193
// NO-OP
193194
}
195+
196+
func (v BoolValue) GetMember(context MemberAccessibleContext, name string, memberKind common.DeclarationKind) Value {
197+
return GetMember(
198+
context,
199+
v,
200+
name,
201+
memberKind,
202+
nil,
203+
)
204+
}
205+
206+
func (v BoolValue) GetMethod(context MemberAccessibleContext, name string) FunctionValue {
207+
switch name {
208+
case sema.ToStringFunctionName:
209+
return NewBoundHostFunctionValue(
210+
context,
211+
v,
212+
sema.ToStringFunctionType,
213+
NativeBoolValueToStringFunction,
214+
)
215+
}
216+
217+
return nil
218+
}
219+
220+
var TrueStringValue = NewUnmeteredStringValue("true")
221+
var FalseStringValue = NewUnmeteredStringValue("false")
222+
223+
var NativeBoolValueToStringFunction = NativeFunction(
224+
func(
225+
context NativeFunctionContext,
226+
_ TypeArgumentsIterator,
227+
_ ArgumentTypesIterator,
228+
receiver Value,
229+
_ []Value,
230+
) Value {
231+
if AssertValueOfType[BoolValue](receiver) {
232+
return TrueStringValue
233+
}
234+
return FalseStringValue
235+
},
236+
)
237+
238+
func (BoolValue) RemoveMember(_ ValueTransferContext, _ string) Value {
239+
// Bool has no removable members (fields / functions)
240+
panic(errors.NewUnreachableError())
241+
}
242+
243+
func (BoolValue) SetMember(_ ValueTransferContext, _ string, _ Value) bool {
244+
// Bool has no settable members (fields / functions)
245+
panic(errors.NewUnreachableError())
246+
}

sema/builtinfunctions_test.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,11 @@ func TestCheckToString(t *testing.T) {
3737

3838
for _, numberOrAddressType := range common.Concat(
3939
sema.AllNumberTypes,
40+
sema.AllPathTypes,
4041
[]sema.Type{
4142
sema.TheAddressType,
43+
sema.BoolType,
44+
sema.CharacterType,
4245
},
4346
) {
4447

sema/check_string_template_expression.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,13 @@ func (checker *Checker) VisitStringTemplateExpression(stringTemplateExpression *
4646
for _, element := range stringTemplateExpression.Expressions {
4747
valueType := checker.VisitExpression(element, stringTemplateExpression, elementType)
4848

49-
if !isValidStringTemplateValue(valueType) {
49+
if !valueType.IsInvalidType() &&
50+
!isValidStringTemplateValue(valueType) {
51+
5052
checker.report(
5153
&TypeMismatchWithDescriptionError{
5254
ActualType: valueType,
53-
ExpectedTypeDescription: "a type with built-in toString() or bool",
55+
ExpectedTypeDescription: "a built-in type with toString()",
5456
Range: ast.NewRangeFromPositioned(checker.memoryGauge, element),
5557
},
5658
)

sema/string_test.go

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -727,6 +727,18 @@ func TestCheckStringTemplate(t *testing.T) {
727727
require.NoError(t, err)
728728
})
729729

730+
t.Run("valid, bool", func(t *testing.T) {
731+
732+
t.Parallel()
733+
734+
_, err := ParseAndCheck(t, `
735+
let a = true
736+
let x: String = "The value of a is: \(a)"
737+
`)
738+
739+
require.NoError(t, err)
740+
})
741+
730742
t.Run("invalid, struct", func(t *testing.T) {
731743

732744
t.Parallel()
@@ -785,10 +797,9 @@ func TestCheckStringTemplate(t *testing.T) {
785797
let x: String = "\(a)"
786798
`)
787799

788-
errs := RequireCheckerErrors(t, err, 2)
800+
errs := RequireCheckerErrors(t, err, 1)
789801

790802
assert.IsType(t, &sema.NotDeclaredError{}, errs[0])
791-
assert.IsType(t, &sema.TypeMismatchWithDescriptionError{}, errs[1])
792803
})
793804

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

sema/type.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -808,7 +808,8 @@ func withBuiltinMembers(ty Type, members map[string]MemberResolver) map[string]M
808808

809809
func HasToStringFunction(ty Type) bool {
810810
switch ty {
811-
case CharacterType:
811+
case BoolType,
812+
CharacterType:
812813
return true
813814
default:
814815
return IsSubType(ty, NumberType) ||
@@ -4712,6 +4713,14 @@ var AllNumberTypes = common.Concat(
47124713
},
47134714
)
47144715

4716+
var AllPathTypes = []Type{
4717+
PathType,
4718+
StoragePathType,
4719+
CapabilityPathType,
4720+
PublicPathType,
4721+
PrivatePathType,
4722+
}
4723+
47154724
var BuiltinEntitlements = map[string]*EntitlementType{}
47164725

47174726
var BuiltinEntitlementMappings = map[string]*EntitlementMapType{

0 commit comments

Comments
 (0)