From ec147bd717806699d37e180e7763150158de794d Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Tue, 20 Jun 2023 00:44:58 +0530 Subject: [PATCH 001/121] Introduce InclusiveRange --- runtime/common/memorykind.go | 2 + runtime/common/memorykind_string.go | 310 +++++++++--------- runtime/common/metering.go | 37 ++- runtime/convertTypes.go | 17 + runtime/format/range.go | 27 ++ runtime/interpreter/decode.go | 14 + runtime/interpreter/encode.go | 20 ++ runtime/interpreter/encoding_test.go | 2 +- runtime/interpreter/statictype.go | 58 ++++ runtime/interpreter/value.go | 27 +- runtime/interpreter/value_range.go | 245 ++++++++++++++ runtime/sema/type.go | 202 ++++++++++++ runtime/sema/type_tags.go | 50 ++- runtime/stdlib/builtin.go | 1 + runtime/stdlib/range.go | 133 ++++++++ runtime/tests/checker/range_value_test.go | 96 ++++++ runtime/tests/interpreter/range_value_test.go | 105 ++++++ types.go | 46 +++ 18 files changed, 1210 insertions(+), 182 deletions(-) create mode 100644 runtime/format/range.go create mode 100644 runtime/interpreter/value_range.go create mode 100644 runtime/stdlib/range.go create mode 100644 runtime/tests/checker/range_value_test.go create mode 100644 runtime/tests/interpreter/range_value_test.go diff --git a/runtime/common/memorykind.go b/runtime/common/memorykind.go index bf1888009f..357ff2a712 100644 --- a/runtime/common/memorykind.go +++ b/runtime/common/memorykind.go @@ -72,6 +72,7 @@ const ( MemoryKindVariableSizedStaticType MemoryKindConstantSizedStaticType MemoryKindDictionaryStaticType + MemoryKindInclusiveRangeStaticType MemoryKindOptionalStaticType MemoryKindRestrictedStaticType MemoryKindReferenceStaticType @@ -116,6 +117,7 @@ const ( MemoryKindCadenceVariableSizedArrayType MemoryKindCadenceConstantSizedArrayType MemoryKindCadenceDictionaryType + MemoryKindCadenceInclusiveRangeType MemoryKindCadenceField MemoryKindCadenceParameter MemoryKindCadenceTypeParameter diff --git a/runtime/common/memorykind_string.go b/runtime/common/memorykind_string.go index 2f5fddc90e..5a5580fa4a 100644 --- a/runtime/common/memorykind_string.go +++ b/runtime/common/memorykind_string.go @@ -49,163 +49,165 @@ func _() { _ = x[MemoryKindVariableSizedStaticType-38] _ = x[MemoryKindConstantSizedStaticType-39] _ = x[MemoryKindDictionaryStaticType-40] - _ = x[MemoryKindOptionalStaticType-41] - _ = x[MemoryKindRestrictedStaticType-42] - _ = x[MemoryKindReferenceStaticType-43] - _ = x[MemoryKindCapabilityStaticType-44] - _ = x[MemoryKindFunctionStaticType-45] - _ = x[MemoryKindCadenceVoidValue-46] - _ = x[MemoryKindCadenceOptionalValue-47] - _ = x[MemoryKindCadenceBoolValue-48] - _ = x[MemoryKindCadenceStringValue-49] - _ = x[MemoryKindCadenceCharacterValue-50] - _ = x[MemoryKindCadenceAddressValue-51] - _ = x[MemoryKindCadenceIntValue-52] - _ = x[MemoryKindCadenceNumberValue-53] - _ = x[MemoryKindCadenceArrayValueBase-54] - _ = x[MemoryKindCadenceArrayValueLength-55] - _ = x[MemoryKindCadenceDictionaryValue-56] - _ = x[MemoryKindCadenceKeyValuePair-57] - _ = x[MemoryKindCadenceStructValueBase-58] - _ = x[MemoryKindCadenceStructValueSize-59] - _ = x[MemoryKindCadenceResourceValueBase-60] - _ = x[MemoryKindCadenceAttachmentValueBase-61] - _ = x[MemoryKindCadenceResourceValueSize-62] - _ = x[MemoryKindCadenceAttachmentValueSize-63] - _ = x[MemoryKindCadenceEventValueBase-64] - _ = x[MemoryKindCadenceEventValueSize-65] - _ = x[MemoryKindCadenceContractValueBase-66] - _ = x[MemoryKindCadenceContractValueSize-67] - _ = x[MemoryKindCadenceEnumValueBase-68] - _ = x[MemoryKindCadenceEnumValueSize-69] - _ = x[MemoryKindCadencePathLinkValue-70] - _ = x[MemoryKindCadenceAccountLinkValue-71] - _ = x[MemoryKindCadencePathValue-72] - _ = x[MemoryKindCadenceTypeValue-73] - _ = x[MemoryKindCadenceIDCapabilityValue-74] - _ = x[MemoryKindCadencePathCapabilityValue-75] - _ = x[MemoryKindCadenceFunctionValue-76] - _ = x[MemoryKindCadenceOptionalType-77] - _ = x[MemoryKindCadenceVariableSizedArrayType-78] - _ = x[MemoryKindCadenceConstantSizedArrayType-79] - _ = x[MemoryKindCadenceDictionaryType-80] - _ = x[MemoryKindCadenceField-81] - _ = x[MemoryKindCadenceParameter-82] - _ = x[MemoryKindCadenceTypeParameter-83] - _ = x[MemoryKindCadenceStructType-84] - _ = x[MemoryKindCadenceResourceType-85] - _ = x[MemoryKindCadenceAttachmentType-86] - _ = x[MemoryKindCadenceEventType-87] - _ = x[MemoryKindCadenceContractType-88] - _ = x[MemoryKindCadenceStructInterfaceType-89] - _ = x[MemoryKindCadenceResourceInterfaceType-90] - _ = x[MemoryKindCadenceContractInterfaceType-91] - _ = x[MemoryKindCadenceFunctionType-92] - _ = x[MemoryKindCadenceReferenceType-93] - _ = x[MemoryKindCadenceRestrictedType-94] - _ = x[MemoryKindCadenceCapabilityType-95] - _ = x[MemoryKindCadenceEnumType-96] - _ = x[MemoryKindRawString-97] - _ = x[MemoryKindAddressLocation-98] - _ = x[MemoryKindBytes-99] - _ = x[MemoryKindVariable-100] - _ = x[MemoryKindCompositeTypeInfo-101] - _ = x[MemoryKindCompositeField-102] - _ = x[MemoryKindInvocation-103] - _ = x[MemoryKindStorageMap-104] - _ = x[MemoryKindStorageKey-105] - _ = x[MemoryKindTypeToken-106] - _ = x[MemoryKindErrorToken-107] - _ = x[MemoryKindSpaceToken-108] - _ = x[MemoryKindProgram-109] - _ = x[MemoryKindIdentifier-110] - _ = x[MemoryKindArgument-111] - _ = x[MemoryKindBlock-112] - _ = x[MemoryKindFunctionBlock-113] - _ = x[MemoryKindParameter-114] - _ = x[MemoryKindParameterList-115] - _ = x[MemoryKindTypeParameter-116] - _ = x[MemoryKindTypeParameterList-117] - _ = x[MemoryKindTransfer-118] - _ = x[MemoryKindMembers-119] - _ = x[MemoryKindTypeAnnotation-120] - _ = x[MemoryKindDictionaryEntry-121] - _ = x[MemoryKindFunctionDeclaration-122] - _ = x[MemoryKindCompositeDeclaration-123] - _ = x[MemoryKindAttachmentDeclaration-124] - _ = x[MemoryKindInterfaceDeclaration-125] - _ = x[MemoryKindEnumCaseDeclaration-126] - _ = x[MemoryKindFieldDeclaration-127] - _ = x[MemoryKindTransactionDeclaration-128] - _ = x[MemoryKindImportDeclaration-129] - _ = x[MemoryKindVariableDeclaration-130] - _ = x[MemoryKindSpecialFunctionDeclaration-131] - _ = x[MemoryKindPragmaDeclaration-132] - _ = x[MemoryKindAssignmentStatement-133] - _ = x[MemoryKindBreakStatement-134] - _ = x[MemoryKindContinueStatement-135] - _ = x[MemoryKindEmitStatement-136] - _ = x[MemoryKindExpressionStatement-137] - _ = x[MemoryKindForStatement-138] - _ = x[MemoryKindIfStatement-139] - _ = x[MemoryKindReturnStatement-140] - _ = x[MemoryKindSwapStatement-141] - _ = x[MemoryKindSwitchStatement-142] - _ = x[MemoryKindWhileStatement-143] - _ = x[MemoryKindRemoveStatement-144] - _ = x[MemoryKindBooleanExpression-145] - _ = x[MemoryKindVoidExpression-146] - _ = x[MemoryKindNilExpression-147] - _ = x[MemoryKindStringExpression-148] - _ = x[MemoryKindIntegerExpression-149] - _ = x[MemoryKindFixedPointExpression-150] - _ = x[MemoryKindArrayExpression-151] - _ = x[MemoryKindDictionaryExpression-152] - _ = x[MemoryKindIdentifierExpression-153] - _ = x[MemoryKindInvocationExpression-154] - _ = x[MemoryKindMemberExpression-155] - _ = x[MemoryKindIndexExpression-156] - _ = x[MemoryKindConditionalExpression-157] - _ = x[MemoryKindUnaryExpression-158] - _ = x[MemoryKindBinaryExpression-159] - _ = x[MemoryKindFunctionExpression-160] - _ = x[MemoryKindCastingExpression-161] - _ = x[MemoryKindCreateExpression-162] - _ = x[MemoryKindDestroyExpression-163] - _ = x[MemoryKindReferenceExpression-164] - _ = x[MemoryKindForceExpression-165] - _ = x[MemoryKindPathExpression-166] - _ = x[MemoryKindAttachExpression-167] - _ = x[MemoryKindConstantSizedType-168] - _ = x[MemoryKindDictionaryType-169] - _ = x[MemoryKindFunctionType-170] - _ = x[MemoryKindInstantiationType-171] - _ = x[MemoryKindNominalType-172] - _ = x[MemoryKindOptionalType-173] - _ = x[MemoryKindReferenceType-174] - _ = x[MemoryKindRestrictedType-175] - _ = x[MemoryKindVariableSizedType-176] - _ = x[MemoryKindPosition-177] - _ = x[MemoryKindRange-178] - _ = x[MemoryKindElaboration-179] - _ = x[MemoryKindActivation-180] - _ = x[MemoryKindActivationEntries-181] - _ = x[MemoryKindVariableSizedSemaType-182] - _ = x[MemoryKindConstantSizedSemaType-183] - _ = x[MemoryKindDictionarySemaType-184] - _ = x[MemoryKindOptionalSemaType-185] - _ = x[MemoryKindRestrictedSemaType-186] - _ = x[MemoryKindReferenceSemaType-187] - _ = x[MemoryKindCapabilitySemaType-188] - _ = x[MemoryKindOrderedMap-189] - _ = x[MemoryKindOrderedMapEntryList-190] - _ = x[MemoryKindOrderedMapEntry-191] - _ = x[MemoryKindLast-192] + _ = x[MemoryKindInclusiveRangeStaticType-41] + _ = x[MemoryKindOptionalStaticType-42] + _ = x[MemoryKindRestrictedStaticType-43] + _ = x[MemoryKindReferenceStaticType-44] + _ = x[MemoryKindCapabilityStaticType-45] + _ = x[MemoryKindFunctionStaticType-46] + _ = x[MemoryKindCadenceVoidValue-47] + _ = x[MemoryKindCadenceOptionalValue-48] + _ = x[MemoryKindCadenceBoolValue-49] + _ = x[MemoryKindCadenceStringValue-50] + _ = x[MemoryKindCadenceCharacterValue-51] + _ = x[MemoryKindCadenceAddressValue-52] + _ = x[MemoryKindCadenceIntValue-53] + _ = x[MemoryKindCadenceNumberValue-54] + _ = x[MemoryKindCadenceArrayValueBase-55] + _ = x[MemoryKindCadenceArrayValueLength-56] + _ = x[MemoryKindCadenceDictionaryValue-57] + _ = x[MemoryKindCadenceKeyValuePair-58] + _ = x[MemoryKindCadenceStructValueBase-59] + _ = x[MemoryKindCadenceStructValueSize-60] + _ = x[MemoryKindCadenceResourceValueBase-61] + _ = x[MemoryKindCadenceAttachmentValueBase-62] + _ = x[MemoryKindCadenceResourceValueSize-63] + _ = x[MemoryKindCadenceAttachmentValueSize-64] + _ = x[MemoryKindCadenceEventValueBase-65] + _ = x[MemoryKindCadenceEventValueSize-66] + _ = x[MemoryKindCadenceContractValueBase-67] + _ = x[MemoryKindCadenceContractValueSize-68] + _ = x[MemoryKindCadenceEnumValueBase-69] + _ = x[MemoryKindCadenceEnumValueSize-70] + _ = x[MemoryKindCadencePathLinkValue-71] + _ = x[MemoryKindCadenceAccountLinkValue-72] + _ = x[MemoryKindCadencePathValue-73] + _ = x[MemoryKindCadenceTypeValue-74] + _ = x[MemoryKindCadenceIDCapabilityValue-75] + _ = x[MemoryKindCadencePathCapabilityValue-76] + _ = x[MemoryKindCadenceFunctionValue-77] + _ = x[MemoryKindCadenceOptionalType-78] + _ = x[MemoryKindCadenceVariableSizedArrayType-79] + _ = x[MemoryKindCadenceConstantSizedArrayType-80] + _ = x[MemoryKindCadenceDictionaryType-81] + _ = x[MemoryKindCadenceInclusiveRangeType-82] + _ = x[MemoryKindCadenceField-83] + _ = x[MemoryKindCadenceParameter-84] + _ = x[MemoryKindCadenceTypeParameter-85] + _ = x[MemoryKindCadenceStructType-86] + _ = x[MemoryKindCadenceResourceType-87] + _ = x[MemoryKindCadenceAttachmentType-88] + _ = x[MemoryKindCadenceEventType-89] + _ = x[MemoryKindCadenceContractType-90] + _ = x[MemoryKindCadenceStructInterfaceType-91] + _ = x[MemoryKindCadenceResourceInterfaceType-92] + _ = x[MemoryKindCadenceContractInterfaceType-93] + _ = x[MemoryKindCadenceFunctionType-94] + _ = x[MemoryKindCadenceReferenceType-95] + _ = x[MemoryKindCadenceRestrictedType-96] + _ = x[MemoryKindCadenceCapabilityType-97] + _ = x[MemoryKindCadenceEnumType-98] + _ = x[MemoryKindRawString-99] + _ = x[MemoryKindAddressLocation-100] + _ = x[MemoryKindBytes-101] + _ = x[MemoryKindVariable-102] + _ = x[MemoryKindCompositeTypeInfo-103] + _ = x[MemoryKindCompositeField-104] + _ = x[MemoryKindInvocation-105] + _ = x[MemoryKindStorageMap-106] + _ = x[MemoryKindStorageKey-107] + _ = x[MemoryKindTypeToken-108] + _ = x[MemoryKindErrorToken-109] + _ = x[MemoryKindSpaceToken-110] + _ = x[MemoryKindProgram-111] + _ = x[MemoryKindIdentifier-112] + _ = x[MemoryKindArgument-113] + _ = x[MemoryKindBlock-114] + _ = x[MemoryKindFunctionBlock-115] + _ = x[MemoryKindParameter-116] + _ = x[MemoryKindParameterList-117] + _ = x[MemoryKindTypeParameter-118] + _ = x[MemoryKindTypeParameterList-119] + _ = x[MemoryKindTransfer-120] + _ = x[MemoryKindMembers-121] + _ = x[MemoryKindTypeAnnotation-122] + _ = x[MemoryKindDictionaryEntry-123] + _ = x[MemoryKindFunctionDeclaration-124] + _ = x[MemoryKindCompositeDeclaration-125] + _ = x[MemoryKindAttachmentDeclaration-126] + _ = x[MemoryKindInterfaceDeclaration-127] + _ = x[MemoryKindEnumCaseDeclaration-128] + _ = x[MemoryKindFieldDeclaration-129] + _ = x[MemoryKindTransactionDeclaration-130] + _ = x[MemoryKindImportDeclaration-131] + _ = x[MemoryKindVariableDeclaration-132] + _ = x[MemoryKindSpecialFunctionDeclaration-133] + _ = x[MemoryKindPragmaDeclaration-134] + _ = x[MemoryKindAssignmentStatement-135] + _ = x[MemoryKindBreakStatement-136] + _ = x[MemoryKindContinueStatement-137] + _ = x[MemoryKindEmitStatement-138] + _ = x[MemoryKindExpressionStatement-139] + _ = x[MemoryKindForStatement-140] + _ = x[MemoryKindIfStatement-141] + _ = x[MemoryKindReturnStatement-142] + _ = x[MemoryKindSwapStatement-143] + _ = x[MemoryKindSwitchStatement-144] + _ = x[MemoryKindWhileStatement-145] + _ = x[MemoryKindRemoveStatement-146] + _ = x[MemoryKindBooleanExpression-147] + _ = x[MemoryKindVoidExpression-148] + _ = x[MemoryKindNilExpression-149] + _ = x[MemoryKindStringExpression-150] + _ = x[MemoryKindIntegerExpression-151] + _ = x[MemoryKindFixedPointExpression-152] + _ = x[MemoryKindArrayExpression-153] + _ = x[MemoryKindDictionaryExpression-154] + _ = x[MemoryKindIdentifierExpression-155] + _ = x[MemoryKindInvocationExpression-156] + _ = x[MemoryKindMemberExpression-157] + _ = x[MemoryKindIndexExpression-158] + _ = x[MemoryKindConditionalExpression-159] + _ = x[MemoryKindUnaryExpression-160] + _ = x[MemoryKindBinaryExpression-161] + _ = x[MemoryKindFunctionExpression-162] + _ = x[MemoryKindCastingExpression-163] + _ = x[MemoryKindCreateExpression-164] + _ = x[MemoryKindDestroyExpression-165] + _ = x[MemoryKindReferenceExpression-166] + _ = x[MemoryKindForceExpression-167] + _ = x[MemoryKindPathExpression-168] + _ = x[MemoryKindAttachExpression-169] + _ = x[MemoryKindConstantSizedType-170] + _ = x[MemoryKindDictionaryType-171] + _ = x[MemoryKindFunctionType-172] + _ = x[MemoryKindInstantiationType-173] + _ = x[MemoryKindNominalType-174] + _ = x[MemoryKindOptionalType-175] + _ = x[MemoryKindReferenceType-176] + _ = x[MemoryKindRestrictedType-177] + _ = x[MemoryKindVariableSizedType-178] + _ = x[MemoryKindPosition-179] + _ = x[MemoryKindRange-180] + _ = x[MemoryKindElaboration-181] + _ = x[MemoryKindActivation-182] + _ = x[MemoryKindActivationEntries-183] + _ = x[MemoryKindVariableSizedSemaType-184] + _ = x[MemoryKindConstantSizedSemaType-185] + _ = x[MemoryKindDictionarySemaType-186] + _ = x[MemoryKindOptionalSemaType-187] + _ = x[MemoryKindRestrictedSemaType-188] + _ = x[MemoryKindReferenceSemaType-189] + _ = x[MemoryKindCapabilitySemaType-190] + _ = x[MemoryKindOrderedMap-191] + _ = x[MemoryKindOrderedMapEntryList-192] + _ = x[MemoryKindOrderedMapEntry-193] + _ = x[MemoryKindLast-194] } -const _MemoryKind_name = "UnknownAddressValueStringValueCharacterValueNumberValueArrayValueBaseDictionaryValueBaseCompositeValueBaseSimpleCompositeValueBaseOptionalValueTypeValuePathValueIDCapabilityValuePathCapabilityValuePathLinkValueAccountLinkValueStorageReferenceValueAccountReferenceValueEphemeralReferenceValueInterpretedFunctionValueHostFunctionValueBoundFunctionValueBigIntSimpleCompositeValuePublishedValueStorageCapabilityControllerValueAccountCapabilityControllerValueAtreeArrayDataSlabAtreeArrayMetaDataSlabAtreeArrayElementOverheadAtreeMapDataSlabAtreeMapMetaDataSlabAtreeMapElementOverheadAtreeMapPreAllocatedElementAtreeEncodedSlabPrimitiveStaticTypeCompositeStaticTypeInterfaceStaticTypeVariableSizedStaticTypeConstantSizedStaticTypeDictionaryStaticTypeOptionalStaticTypeRestrictedStaticTypeReferenceStaticTypeCapabilityStaticTypeFunctionStaticTypeCadenceVoidValueCadenceOptionalValueCadenceBoolValueCadenceStringValueCadenceCharacterValueCadenceAddressValueCadenceIntValueCadenceNumberValueCadenceArrayValueBaseCadenceArrayValueLengthCadenceDictionaryValueCadenceKeyValuePairCadenceStructValueBaseCadenceStructValueSizeCadenceResourceValueBaseCadenceAttachmentValueBaseCadenceResourceValueSizeCadenceAttachmentValueSizeCadenceEventValueBaseCadenceEventValueSizeCadenceContractValueBaseCadenceContractValueSizeCadenceEnumValueBaseCadenceEnumValueSizeCadencePathLinkValueCadenceAccountLinkValueCadencePathValueCadenceTypeValueCadenceIDCapabilityValueCadencePathCapabilityValueCadenceFunctionValueCadenceOptionalTypeCadenceVariableSizedArrayTypeCadenceConstantSizedArrayTypeCadenceDictionaryTypeCadenceFieldCadenceParameterCadenceTypeParameterCadenceStructTypeCadenceResourceTypeCadenceAttachmentTypeCadenceEventTypeCadenceContractTypeCadenceStructInterfaceTypeCadenceResourceInterfaceTypeCadenceContractInterfaceTypeCadenceFunctionTypeCadenceReferenceTypeCadenceRestrictedTypeCadenceCapabilityTypeCadenceEnumTypeRawStringAddressLocationBytesVariableCompositeTypeInfoCompositeFieldInvocationStorageMapStorageKeyTypeTokenErrorTokenSpaceTokenProgramIdentifierArgumentBlockFunctionBlockParameterParameterListTypeParameterTypeParameterListTransferMembersTypeAnnotationDictionaryEntryFunctionDeclarationCompositeDeclarationAttachmentDeclarationInterfaceDeclarationEnumCaseDeclarationFieldDeclarationTransactionDeclarationImportDeclarationVariableDeclarationSpecialFunctionDeclarationPragmaDeclarationAssignmentStatementBreakStatementContinueStatementEmitStatementExpressionStatementForStatementIfStatementReturnStatementSwapStatementSwitchStatementWhileStatementRemoveStatementBooleanExpressionVoidExpressionNilExpressionStringExpressionIntegerExpressionFixedPointExpressionArrayExpressionDictionaryExpressionIdentifierExpressionInvocationExpressionMemberExpressionIndexExpressionConditionalExpressionUnaryExpressionBinaryExpressionFunctionExpressionCastingExpressionCreateExpressionDestroyExpressionReferenceExpressionForceExpressionPathExpressionAttachExpressionConstantSizedTypeDictionaryTypeFunctionTypeInstantiationTypeNominalTypeOptionalTypeReferenceTypeRestrictedTypeVariableSizedTypePositionRangeElaborationActivationActivationEntriesVariableSizedSemaTypeConstantSizedSemaTypeDictionarySemaTypeOptionalSemaTypeRestrictedSemaTypeReferenceSemaTypeCapabilitySemaTypeOrderedMapOrderedMapEntryListOrderedMapEntryLast" +const _MemoryKind_name = "UnknownAddressValueStringValueCharacterValueNumberValueArrayValueBaseDictionaryValueBaseCompositeValueBaseSimpleCompositeValueBaseOptionalValueTypeValuePathValueIDCapabilityValuePathCapabilityValuePathLinkValueAccountLinkValueStorageReferenceValueAccountReferenceValueEphemeralReferenceValueInterpretedFunctionValueHostFunctionValueBoundFunctionValueBigIntSimpleCompositeValuePublishedValueStorageCapabilityControllerValueAccountCapabilityControllerValueAtreeArrayDataSlabAtreeArrayMetaDataSlabAtreeArrayElementOverheadAtreeMapDataSlabAtreeMapMetaDataSlabAtreeMapElementOverheadAtreeMapPreAllocatedElementAtreeEncodedSlabPrimitiveStaticTypeCompositeStaticTypeInterfaceStaticTypeVariableSizedStaticTypeConstantSizedStaticTypeDictionaryStaticTypeInclusiveRangeStaticTypeOptionalStaticTypeRestrictedStaticTypeReferenceStaticTypeCapabilityStaticTypeFunctionStaticTypeCadenceVoidValueCadenceOptionalValueCadenceBoolValueCadenceStringValueCadenceCharacterValueCadenceAddressValueCadenceIntValueCadenceNumberValueCadenceArrayValueBaseCadenceArrayValueLengthCadenceDictionaryValueCadenceKeyValuePairCadenceStructValueBaseCadenceStructValueSizeCadenceResourceValueBaseCadenceAttachmentValueBaseCadenceResourceValueSizeCadenceAttachmentValueSizeCadenceEventValueBaseCadenceEventValueSizeCadenceContractValueBaseCadenceContractValueSizeCadenceEnumValueBaseCadenceEnumValueSizeCadencePathLinkValueCadenceAccountLinkValueCadencePathValueCadenceTypeValueCadenceIDCapabilityValueCadencePathCapabilityValueCadenceFunctionValueCadenceOptionalTypeCadenceVariableSizedArrayTypeCadenceConstantSizedArrayTypeCadenceDictionaryTypeCadenceInclusiveRangeTypeCadenceFieldCadenceParameterCadenceTypeParameterCadenceStructTypeCadenceResourceTypeCadenceAttachmentTypeCadenceEventTypeCadenceContractTypeCadenceStructInterfaceTypeCadenceResourceInterfaceTypeCadenceContractInterfaceTypeCadenceFunctionTypeCadenceReferenceTypeCadenceRestrictedTypeCadenceCapabilityTypeCadenceEnumTypeRawStringAddressLocationBytesVariableCompositeTypeInfoCompositeFieldInvocationStorageMapStorageKeyTypeTokenErrorTokenSpaceTokenProgramIdentifierArgumentBlockFunctionBlockParameterParameterListTypeParameterTypeParameterListTransferMembersTypeAnnotationDictionaryEntryFunctionDeclarationCompositeDeclarationAttachmentDeclarationInterfaceDeclarationEnumCaseDeclarationFieldDeclarationTransactionDeclarationImportDeclarationVariableDeclarationSpecialFunctionDeclarationPragmaDeclarationAssignmentStatementBreakStatementContinueStatementEmitStatementExpressionStatementForStatementIfStatementReturnStatementSwapStatementSwitchStatementWhileStatementRemoveStatementBooleanExpressionVoidExpressionNilExpressionStringExpressionIntegerExpressionFixedPointExpressionArrayExpressionDictionaryExpressionIdentifierExpressionInvocationExpressionMemberExpressionIndexExpressionConditionalExpressionUnaryExpressionBinaryExpressionFunctionExpressionCastingExpressionCreateExpressionDestroyExpressionReferenceExpressionForceExpressionPathExpressionAttachExpressionConstantSizedTypeDictionaryTypeFunctionTypeInstantiationTypeNominalTypeOptionalTypeReferenceTypeRestrictedTypeVariableSizedTypePositionRangeElaborationActivationActivationEntriesVariableSizedSemaTypeConstantSizedSemaTypeDictionarySemaTypeOptionalSemaTypeRestrictedSemaTypeReferenceSemaTypeCapabilitySemaTypeOrderedMapOrderedMapEntryListOrderedMapEntryLast" -var _MemoryKind_index = [...]uint16{0, 7, 19, 30, 44, 55, 69, 88, 106, 130, 143, 152, 161, 178, 197, 210, 226, 247, 268, 291, 315, 332, 350, 356, 376, 390, 422, 454, 472, 494, 519, 535, 555, 578, 605, 621, 640, 659, 678, 701, 724, 744, 762, 782, 801, 821, 839, 855, 875, 891, 909, 930, 949, 964, 982, 1003, 1026, 1048, 1067, 1089, 1111, 1135, 1161, 1185, 1211, 1232, 1253, 1277, 1301, 1321, 1341, 1361, 1384, 1400, 1416, 1440, 1466, 1486, 1505, 1534, 1563, 1584, 1596, 1612, 1632, 1649, 1668, 1689, 1705, 1724, 1750, 1778, 1806, 1825, 1845, 1866, 1887, 1902, 1911, 1926, 1931, 1939, 1956, 1970, 1980, 1990, 2000, 2009, 2019, 2029, 2036, 2046, 2054, 2059, 2072, 2081, 2094, 2107, 2124, 2132, 2139, 2153, 2168, 2187, 2207, 2228, 2248, 2267, 2283, 2305, 2322, 2341, 2367, 2384, 2403, 2417, 2434, 2447, 2466, 2478, 2489, 2504, 2517, 2532, 2546, 2561, 2578, 2592, 2605, 2621, 2638, 2658, 2673, 2693, 2713, 2733, 2749, 2764, 2785, 2800, 2816, 2834, 2851, 2867, 2884, 2903, 2918, 2932, 2948, 2965, 2979, 2991, 3008, 3019, 3031, 3044, 3058, 3075, 3083, 3088, 3099, 3109, 3126, 3147, 3168, 3186, 3202, 3220, 3237, 3255, 3265, 3284, 3299, 3303} +var _MemoryKind_index = [...]uint16{0, 7, 19, 30, 44, 55, 69, 88, 106, 130, 143, 152, 161, 178, 197, 210, 226, 247, 268, 291, 315, 332, 350, 356, 376, 390, 422, 454, 472, 494, 519, 535, 555, 578, 605, 621, 640, 659, 678, 701, 724, 744, 768, 786, 806, 825, 845, 863, 879, 899, 915, 933, 954, 973, 988, 1006, 1027, 1050, 1072, 1091, 1113, 1135, 1159, 1185, 1209, 1235, 1256, 1277, 1301, 1325, 1345, 1365, 1385, 1408, 1424, 1440, 1464, 1490, 1510, 1529, 1558, 1587, 1608, 1633, 1645, 1661, 1681, 1698, 1717, 1738, 1754, 1773, 1799, 1827, 1855, 1874, 1894, 1915, 1936, 1951, 1960, 1975, 1980, 1988, 2005, 2019, 2029, 2039, 2049, 2058, 2068, 2078, 2085, 2095, 2103, 2108, 2121, 2130, 2143, 2156, 2173, 2181, 2188, 2202, 2217, 2236, 2256, 2277, 2297, 2316, 2332, 2354, 2371, 2390, 2416, 2433, 2452, 2466, 2483, 2496, 2515, 2527, 2538, 2553, 2566, 2581, 2595, 2610, 2627, 2641, 2654, 2670, 2687, 2707, 2722, 2742, 2762, 2782, 2798, 2813, 2834, 2849, 2865, 2883, 2900, 2916, 2933, 2952, 2967, 2981, 2997, 3014, 3028, 3040, 3057, 3068, 3080, 3093, 3107, 3124, 3132, 3137, 3148, 3158, 3175, 3196, 3217, 3235, 3251, 3269, 3286, 3304, 3314, 3333, 3348, 3352} func (i MemoryKind) String() string { if i >= MemoryKind(len(_MemoryKind_index)-1) { diff --git a/runtime/common/metering.go b/runtime/common/metering.go index 9135d75b01..c3b41e1bd6 100644 --- a/runtime/common/metering.go +++ b/runtime/common/metering.go @@ -159,17 +159,18 @@ var ( // Static Types - PrimitiveStaticTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindPrimitiveStaticType) - CompositeStaticTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindCompositeStaticType) - InterfaceStaticTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindInterfaceStaticType) - VariableSizedStaticTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindVariableSizedStaticType) - ConstantSizedStaticTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindConstantSizedStaticType) - DictionaryStaticTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindDictionaryStaticType) - OptionalStaticTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindOptionalStaticType) - RestrictedStaticTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindRestrictedStaticType) - ReferenceStaticTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindReferenceStaticType) - CapabilityStaticTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindCapabilityStaticType) - FunctionStaticTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindFunctionStaticType) + PrimitiveStaticTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindPrimitiveStaticType) + CompositeStaticTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindCompositeStaticType) + InterfaceStaticTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindInterfaceStaticType) + VariableSizedStaticTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindVariableSizedStaticType) + ConstantSizedStaticTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindConstantSizedStaticType) + DictionaryStaticTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindDictionaryStaticType) + InclusiveRangeStaticTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindInclusiveRangeStaticType) + OptionalStaticTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindOptionalStaticType) + RestrictedStaticTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindRestrictedStaticType) + ReferenceStaticTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindReferenceStaticType) + CapabilityStaticTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindCapabilityStaticType) + FunctionStaticTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindFunctionStaticType) // Sema types @@ -219,6 +220,7 @@ var ( CadenceContractInterfaceTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindCadenceContractInterfaceType) CadenceContractTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindCadenceContractType) CadenceDictionaryTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindCadenceDictionaryType) + CadenceInclusiveRangeTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindCadenceInclusiveRangeType) CadenceEnumTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindCadenceEnumType) CadenceEventTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindCadenceEventType) CadenceFunctionTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindCadenceFunctionType) @@ -264,12 +266,13 @@ var ( // Static types string representations - VariableSizedStaticTypeStringMemoryUsage = NewRawStringMemoryUsage(2) // [] - DictionaryStaticTypeStringMemoryUsage = NewRawStringMemoryUsage(4) // {: } - OptionalStaticTypeStringMemoryUsage = NewRawStringMemoryUsage(1) // ? - AuthReferenceStaticTypeStringMemoryUsage = NewRawStringMemoryUsage(5) // auth& - ReferenceStaticTypeStringMemoryUsage = NewRawStringMemoryUsage(1) // & - CapabilityStaticTypeStringMemoryUsage = NewRawStringMemoryUsage(12) // Capability<> + VariableSizedStaticTypeStringMemoryUsage = NewRawStringMemoryUsage(2) // [] + DictionaryStaticTypeStringMemoryUsage = NewRawStringMemoryUsage(4) // {: } + InclusiveRangeStaticTypeStringMemoryUsage = NewRawStringMemoryUsage(16) // InclusiveRange<> + OptionalStaticTypeStringMemoryUsage = NewRawStringMemoryUsage(1) // ? + AuthReferenceStaticTypeStringMemoryUsage = NewRawStringMemoryUsage(5) // auth& + ReferenceStaticTypeStringMemoryUsage = NewRawStringMemoryUsage(1) // & + CapabilityStaticTypeStringMemoryUsage = NewRawStringMemoryUsage(12) // Capability<> ) func UseMemory(gauge MemoryGauge, usage MemoryUsage) { diff --git a/runtime/convertTypes.go b/runtime/convertTypes.go index c51c987987..128b0f6d67 100644 --- a/runtime/convertTypes.go +++ b/runtime/convertTypes.go @@ -62,6 +62,8 @@ func ExportMeteredType( return exportInterfaceType(gauge, t, results) case *sema.DictionaryType: return exportDictionaryType(gauge, t, results) + case *sema.InclusiveRangeType: + return exportInclusiveRangeType(gauge, t, results) case *sema.FunctionType: return exportFunctionType(gauge, t, results) case *sema.AddressType: @@ -402,6 +404,19 @@ func exportDictionaryType( ) } +func exportInclusiveRangeType( + gauge common.MemoryGauge, + t *sema.InclusiveRangeType, + results map[sema.TypeID]cadence.Type, +) cadence.Type { + convertedMemberType := ExportMeteredType(gauge, t.MemberType, results) + + return cadence.NewMeteredInclusiveRangeType( + gauge, + convertedMemberType, + ) +} + func exportFunctionType( gauge common.MemoryGauge, t *sema.FunctionType, @@ -628,6 +643,8 @@ func ImportType(memoryGauge common.MemoryGauge, t cadence.Type) interpreter.Stat ImportType(memoryGauge, t.KeyType), ImportType(memoryGauge, t.ElementType), ) + case *cadence.InclusiveRangeType: + return interpreter.NewInclusiveRangeStaticType(memoryGauge, ImportType(memoryGauge, t.ElementType)) case *cadence.StructType, *cadence.ResourceType, *cadence.EventType, diff --git a/runtime/format/range.go b/runtime/format/range.go new file mode 100644 index 0000000000..93d1b1ee73 --- /dev/null +++ b/runtime/format/range.go @@ -0,0 +1,27 @@ +/* + * Cadence - The resource-oriented smart contract programming language + * + * Copyright Dapper Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package format + +import ( + "fmt" +) + +func InclusiveRange(start, end string) string { + return fmt.Sprintf("InclusiveRange{ start: %s, end: %s }", start, end) +} diff --git a/runtime/interpreter/decode.go b/runtime/interpreter/decode.go index 1fc2dd56a8..3566479ce2 100644 --- a/runtime/interpreter/decode.go +++ b/runtime/interpreter/decode.go @@ -1428,6 +1428,9 @@ func (d TypeDecoder) DecodeStaticType() (StaticType, error) { case CBORTagDictionaryStaticType: return d.decodeDictionaryStaticType() + case CBORTagInclusiveRangeStaticType: + return d.decodeInclusiveRangeStaticType() + case CBORTagRestrictedStaticType: return d.decodeRestrictedStaticType() @@ -1730,6 +1733,17 @@ func (d TypeDecoder) decodeDictionaryStaticType() (StaticType, error) { return NewDictionaryStaticType(d.memoryGauge, keyType, valueType), nil } +func (d TypeDecoder) decodeInclusiveRangeStaticType() (StaticType, error) { + elementType, err := d.DecodeStaticType() + if err != nil { + return nil, errors.NewUnexpectedError( + "invalid inclusive range static type encoding: %w", + err, + ) + } + return NewInclusiveRangeStaticType(d.memoryGauge, elementType), nil +} + func (d TypeDecoder) decodeRestrictedStaticType() (StaticType, error) { const expectedLength = encodedRestrictedStaticTypeLength diff --git a/runtime/interpreter/encode.go b/runtime/interpreter/encode.go index 26565e4319..e199244140 100644 --- a/runtime/interpreter/encode.go +++ b/runtime/interpreter/encode.go @@ -218,6 +218,7 @@ const ( CBORTagReferenceStaticType CBORTagRestrictedStaticType CBORTagCapabilityStaticType + CBORTagInclusiveRangeStaticType // !!! *WARNING* !!! // ADD NEW TYPES *BEFORE* THIS WARNING. @@ -1496,6 +1497,25 @@ func (t DictionaryStaticType) Encode(e *cbor.StreamEncoder) error { return t.ValueType.Encode(e) } +// Encode encodes InclusiveRangeStaticType as +// +// cbor.Tag{ +// Number: CBORTagInclusiveRangeStaticType, +// Content: StaticType(v.Type), +// } +func (t InclusiveRangeStaticType) Encode(e *cbor.StreamEncoder) error { + // Encode tag number and array head + err := e.EncodeRawBytes([]byte{ + // tag number + 0xd8, CBORTagInclusiveRangeStaticType, + }) + if err != nil { + return err + } + + return t.ElementType.Encode(e) +} + // NOTE: NEVER change, only add/increment; ensure uint64 const ( // encodedRestrictedStaticTypeTypeFieldKey uint64 = 0 diff --git a/runtime/interpreter/encoding_test.go b/runtime/interpreter/encoding_test.go index e0676178f8..907dd8a123 100644 --- a/runtime/interpreter/encoding_test.go +++ b/runtime/interpreter/encoding_test.go @@ -4334,7 +4334,7 @@ func TestCBORTagValue(t *testing.T) { t.Parallel() t.Run("No new types added in between", func(t *testing.T) { - require.Equal(t, byte(222), byte(CBORTag_Count)) + require.Equal(t, byte(223), byte(CBORTag_Count)) }) } diff --git a/runtime/interpreter/statictype.go b/runtime/interpreter/statictype.go index 76c0f7a221..2438c891b9 100644 --- a/runtime/interpreter/statictype.go +++ b/runtime/interpreter/statictype.go @@ -230,6 +230,53 @@ func (t VariableSizedStaticType) Equal(other StaticType) bool { return t.Type.Equal(otherVariableSizedType.Type) } +// InclusiveRangeStaticType + +type InclusiveRangeStaticType struct { + ElementType StaticType +} + +var _ StaticType = InclusiveRangeStaticType{} +var _ atree.TypeInfo = InclusiveRangeStaticType{} + +func NewInclusiveRangeStaticType( + memoryGauge common.MemoryGauge, + elementType StaticType, +) InclusiveRangeStaticType { + common.UseMemory(memoryGauge, common.InclusiveRangeStaticTypeMemoryUsage) + + return InclusiveRangeStaticType{ + ElementType: elementType, + } +} + +func (InclusiveRangeStaticType) isStaticType() {} + +func (InclusiveRangeStaticType) elementSize() uint { + return UnknownElementSize +} + +func (t InclusiveRangeStaticType) String() string { + return fmt.Sprintf("InclusiveRange<%s>", t.ElementType) +} + +func (t InclusiveRangeStaticType) MeteredString(memoryGauge common.MemoryGauge) string { + common.UseMemory(memoryGauge, common.InclusiveRangeStaticTypeStringMemoryUsage) + + elementStr := t.ElementType.MeteredString(memoryGauge) + + return fmt.Sprintf("InclusiveRange<%s>", elementStr) +} + +func (t InclusiveRangeStaticType) Equal(other StaticType) bool { + otherRangeType, ok := other.(InclusiveRangeStaticType) + if !ok { + return false + } + + return t.ElementType.Equal(otherRangeType.ElementType) +} + // ConstantSizedStaticType type ConstantSizedStaticType struct { @@ -769,6 +816,17 @@ func ConvertStaticToSemaType( valueType, ), nil + case InclusiveRangeStaticType: + elementType, err := ConvertStaticToSemaType(memoryGauge, t.ElementType, getInterface, getComposite) + if err != nil { + return nil, err + } + + return sema.NewInclusiveRangeType( + memoryGauge, + elementType, + ), nil + case OptionalStaticType: ty, err := ConvertStaticToSemaType(memoryGauge, t.Type, getInterface, getComposite) if err != nil { diff --git a/runtime/interpreter/value.go b/runtime/interpreter/value.go index 12efe97254..aef3904c73 100644 --- a/runtime/interpreter/value.go +++ b/runtime/interpreter/value.go @@ -16234,8 +16234,12 @@ func (UFix64Value) Scale() int { // CompositeValue type CompositeValue struct { - Destructor FunctionValue - Location common.Location + Destructor FunctionValue + Location common.Location + + // note that the staticType is not guaranteed to be a CompositeStaticType as there can be types + // which are non-composite but their values are treated as CompositeValue. + // For e.g. InclusiveRangeValue staticType StaticType Stringer func(gauge common.MemoryGauge, value *CompositeValue, seenReferences SeenReferences) string InjectedFields map[string]Value @@ -16279,6 +16283,25 @@ func NewUnmeteredCompositeField(name string, value Value) CompositeField { } } +// Create a CompositeValue with the provided StaticType. +// Useful when we wish to utilize CompositeValue as the value +// for a type which isn't CompositeType. +// For e.g. RangeType +func NewCompositeValueWithStaticType( + interpreter *Interpreter, + locationRange LocationRange, + location common.Location, + qualifiedIdentifier string, + kind common.CompositeKind, + fields []CompositeField, + address common.Address, + staticType StaticType, +) *CompositeValue { + value := NewCompositeValue(interpreter, locationRange, location, qualifiedIdentifier, kind, fields, address) + value.staticType = staticType + return value +} + func NewCompositeValue( interpreter *Interpreter, locationRange LocationRange, diff --git a/runtime/interpreter/value_range.go b/runtime/interpreter/value_range.go new file mode 100644 index 0000000000..ba80a8983f --- /dev/null +++ b/runtime/interpreter/value_range.go @@ -0,0 +1,245 @@ +/* + * Cadence - The resource-oriented smart contract programming language + * + * Copyright Dapper Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package interpreter + +import ( + "github.com/onflow/cadence/runtime/common" + "github.com/onflow/cadence/runtime/errors" + "github.com/onflow/cadence/runtime/sema" +) + +// NewInclusiveRangeValue constructs a InclusiveRange value with the provided start, end with default value of step. +func NewInclusiveRangeValue( + interpreter *Interpreter, + locationRange LocationRange, + start IntegerValue, + end IntegerValue, + rangeType InclusiveRangeStaticType, +) *CompositeValue { + startComparable, startOk := start.(ComparableValue) + endInclusiveComparable, endInclusiveOk := end.(ComparableValue) + if !startOk || !endInclusiveOk { + panic(errors.NewUnreachableError()) + } + + step := getValueForIntegerType(1, rangeType.ElementType) + if startComparable.Greater(interpreter, endInclusiveComparable, locationRange) { + negatedStep, ok := step.Negate(interpreter, locationRange).(IntegerValue) + if !ok { + panic(errors.NewUnreachableError()) + } + + step = negatedStep + } + + return NewInclusiveRangeValueWithStep(interpreter, locationRange, start, end, step, rangeType) +} + +// NewInclusiveRangeValue constructs a InclusiveRange value with the provided start, end & step. +func NewInclusiveRangeValueWithStep( + interpreter *Interpreter, + locationRange LocationRange, + start IntegerValue, + end IntegerValue, + step IntegerValue, + rangeType InclusiveRangeStaticType, +) *CompositeValue { + + // TODO: Validate if the sequence is moving away from the end value. + // Also validate that step is non-zero. + + fields := []CompositeField{ + { + Name: sema.InclusiveRangeTypeStartFieldName, + Value: start, + }, + { + Name: sema.InclusiveRangeTypeEndInclusiveFieldName, + Value: end, + }, + { + Name: sema.InclusiveRangeTypeStepFieldName, + Value: step, + }, + } + + rangeSemaType := getInclusiveRangeSemaType(interpreter, rangeType) + + rangeValue := NewCompositeValueWithStaticType( + interpreter, + locationRange, + sema.PublicKeyType.Location, // TODO: Figure out a location. + rangeSemaType.QualifiedString(), + common.CompositeKindStructure, + fields, + common.ZeroAddress, + rangeType, + ) + + rangeValue.ComputedFields = map[string]ComputedField{ + sema.InclusiveRangeTypeCountFieldName: func(interpreter *Interpreter, locationRange LocationRange) Value { + start := getFieldAsIntegerValue(rangeValue, interpreter, locationRange, sema.InclusiveRangeTypeStartFieldName) + endInclusive := getFieldAsIntegerValue(rangeValue, interpreter, locationRange, sema.InclusiveRangeTypeEndInclusiveFieldName) + step := getFieldAsIntegerValue(rangeValue, interpreter, locationRange, sema.InclusiveRangeTypeStepFieldName) + + diff := convertAndAssertIntegerValue(endInclusive.Minus(interpreter, start, locationRange)) + + // Perform integer division & drop the decimal part. + // Note that step is guaranteed to be non-zero. + return diff.Div(interpreter, step, locationRange) + }, + } + rangeValue.Functions = map[string]FunctionValue{ + sema.InclusiveRangeTypeContainsFunctionName: NewHostFunctionValue( + interpreter, + sema.InclusiveRangeContainsFunctionType( + rangeSemaType.MemberType, + ), + func(invocation Invocation) Value { + return rangeContains( + rangeValue, + rangeType, + invocation.Interpreter, + invocation.LocationRange, + invocation.Arguments[0], + ) + }, + ), + } + + return rangeValue +} + +func getInclusiveRangeSemaType(interpreter *Interpreter, rangeType InclusiveRangeStaticType) *sema.InclusiveRangeType { + return interpreter.MustConvertStaticToSemaType(rangeType).(*sema.InclusiveRangeType) +} + +func rangeContains( + rangeValue *CompositeValue, + rangeType InclusiveRangeStaticType, + interpreter *Interpreter, + locationRange LocationRange, + needleValue Value, +) BoolValue { + start := getFieldAsIntegerValue(rangeValue, interpreter, locationRange, sema.InclusiveRangeTypeStartFieldName) + endInclusive := getFieldAsIntegerValue(rangeValue, interpreter, locationRange, sema.InclusiveRangeTypeEndInclusiveFieldName) + step := getFieldAsIntegerValue(rangeValue, interpreter, locationRange, sema.InclusiveRangeTypeStepFieldName) + + needleInteger := convertAndAssertIntegerValue(needleValue) + + var result bool + result = start.Equal(interpreter, locationRange, needleInteger) || + endInclusive.Equal(interpreter, locationRange, needleInteger) + + if !result { + greaterThanStart := needleInteger.Greater(interpreter, start, locationRange) + greaterThanEndInclusive := needleInteger.Greater(interpreter, endInclusive, locationRange) + + if greaterThanStart == greaterThanEndInclusive { + // If needle is greater or smaller than both start & endInclusive, then it is outside the range. + result = false + } else { + // needle is in between start and endInclusive. + // start + k * step should be equal to needle i.e. (needle - start) mod step == 0. + diff, ok := needleInteger.Minus(interpreter, start, locationRange).(IntegerValue) + if !ok { + panic(errors.NewUnreachableError()) + } + + result = diff.Mod(interpreter, step, locationRange).Equal(interpreter, locationRange, getValueForIntegerType(0, rangeType.ElementType)) + } + } + + return AsBoolValue(result) +} + +// Get the provided int64 value in the required staticType. +// Note: Assumes that the provided value fits within the constraints of the staticType. +func getValueForIntegerType(value int64, staticType StaticType) IntegerValue { + switch staticType { + case PrimitiveStaticTypeInt: + return NewUnmeteredIntValueFromInt64(value) + case PrimitiveStaticTypeInt8: + return NewUnmeteredInt8Value(int8(value)) + case PrimitiveStaticTypeInt16: + return NewUnmeteredInt16Value(int16(value)) + case PrimitiveStaticTypeInt32: + return NewUnmeteredInt32Value(int32(value)) + case PrimitiveStaticTypeInt64: + return NewUnmeteredInt64Value(value) + case PrimitiveStaticTypeInt128: + return NewUnmeteredInt128ValueFromInt64(value) + case PrimitiveStaticTypeInt256: + return NewUnmeteredInt256ValueFromInt64(value) + + case PrimitiveStaticTypeUInt: + return NewUnmeteredUIntValueFromUint64(uint64(value)) + case PrimitiveStaticTypeUInt8: + return NewUnmeteredUInt8Value(uint8(value)) + case PrimitiveStaticTypeUInt16: + return NewUnmeteredUInt16Value(uint16(value)) + case PrimitiveStaticTypeUInt32: + return NewUnmeteredUInt32Value(uint32(value)) + case PrimitiveStaticTypeUInt64: + return NewUnmeteredUInt64Value(uint64(value)) + case PrimitiveStaticTypeUInt128: + return NewUnmeteredUInt128ValueFromUint64(uint64(value)) + case PrimitiveStaticTypeUInt256: + return NewUnmeteredUInt256ValueFromUint64(uint64(value)) + + case PrimitiveStaticTypeWord8: + return NewUnmeteredWord8Value(uint8(value)) + case PrimitiveStaticTypeWord16: + return NewUnmeteredWord16Value(uint16(value)) + case PrimitiveStaticTypeWord32: + return NewUnmeteredWord32Value(uint32(value)) + case PrimitiveStaticTypeWord64: + return NewUnmeteredWord64Value(uint64(value)) + case PrimitiveStaticTypeWord128: + return NewUnmeteredWord128ValueFromUint64(uint64(value)) + case PrimitiveStaticTypeWord256: + return NewUnmeteredWord256ValueFromUint64(uint64(value)) + + default: + panic(errors.NewUnreachableError()) + } +} + +func getFieldAsIntegerValue( + rangeValue *CompositeValue, + interpreter *Interpreter, + locationRange LocationRange, + name string, +) IntegerValue { + return convertAndAssertIntegerValue( + rangeValue.GetField( + interpreter, + locationRange, + sema.InclusiveRangeTypeStartFieldName, + ), + ) +} + +func convertAndAssertIntegerValue(value Value) IntegerValue { + integerValue, ok := value.(IntegerValue) + if !ok { + panic(errors.NewUnreachableError()) + } + return integerValue +} diff --git a/runtime/sema/type.go b/runtime/sema/type.go index 66c9487ac2..02e52bb735 100644 --- a/runtime/sema/type.go +++ b/runtime/sema/type.go @@ -5222,6 +5222,208 @@ func (t *DictionaryType) Resolve(typeArguments *TypeParameterTypeOrderedMap) Typ } } +// InclusiveRangeType todo. + +type InclusiveRangeType struct { + MemberType Type + memberResolvers map[string]MemberResolver + memberResolversOnce sync.Once +} + +var _ Type = &InclusiveRangeType{} + +func NewInclusiveRangeType(memoryGauge common.MemoryGauge, elementType Type) *InclusiveRangeType { + common.UseMemory(memoryGauge, common.DictionarySemaTypeMemoryUsage) + return &InclusiveRangeType{ + MemberType: elementType, + } +} + +func (*InclusiveRangeType) IsType() {} + +func (*InclusiveRangeType) Tag() TypeTag { + return InclusiveRangeTypeTag +} + +func (r *InclusiveRangeType) String() string { + return fmt.Sprintf( + "InclusiveRange<%s>", + r.MemberType, + ) +} + +func (r *InclusiveRangeType) QualifiedString() string { + return fmt.Sprintf( + "InclusiveRange<%s>", + r.MemberType.QualifiedString(), + ) +} + +func (r *InclusiveRangeType) ID() TypeID { + return TypeID(fmt.Sprintf( + "InclusiveRange<%s>", + r.MemberType.ID(), + )) +} + +func (r *InclusiveRangeType) Equal(other Type) bool { + otherRange, ok := other.(*InclusiveRangeType) + if !ok { + return false + } + + return otherRange.MemberType.Equal(r.MemberType) +} + +func (r *InclusiveRangeType) IsResourceType() bool { + return r.MemberType.IsResourceType() +} + +func (r *InclusiveRangeType) IsInvalidType() bool { + return r.MemberType.IsInvalidType() +} + +func (r *InclusiveRangeType) IsStorable(results map[*Member]bool) bool { + return r.MemberType.IsStorable(results) +} + +func (r *InclusiveRangeType) IsExportable(results map[*Member]bool) bool { + return r.MemberType.IsExportable(results) +} + +func (r *InclusiveRangeType) IsImportable(results map[*Member]bool) bool { + return r.MemberType.IsImportable(results) +} + +func (r *InclusiveRangeType) IsEquatable() bool { + return r.MemberType.IsEquatable() +} + +func (*InclusiveRangeType) IsComparable() bool { + return false +} + +func (r *InclusiveRangeType) TypeAnnotationState() TypeAnnotationState { + elementTypeAnnotationState := r.MemberType.TypeAnnotationState() + if elementTypeAnnotationState != TypeAnnotationStateValid { + return elementTypeAnnotationState + } + + return TypeAnnotationStateValid +} + +func (r *InclusiveRangeType) RewriteWithRestrictedTypes() (Type, bool) { + rewrittenElementType, elementTypeRewritten := r.MemberType.RewriteWithRestrictedTypes() + if elementTypeRewritten { + return &InclusiveRangeType{ + MemberType: rewrittenElementType, + }, true + } else { + return r, false + } +} + +const InclusiveRangeTypeStartFieldName = "start" +const InclusiveRangeTypeEndInclusiveFieldName = "endInclusive" +const InclusiveRangeTypeStepFieldName = "step" +const InclusiveRangeTypeCountFieldName = "count" + +const inclusiveRangeTypeCountFieldDocString = ` +The number of entries in the Range sequence +` + +const InclusiveRangeTypeContainsFunctionName = "contains" + +const inclusiveRangeTypeContainsFunctionDocString = ` +Returns true if the given integer is in the InclusiveRange sequence +` + +func (r *InclusiveRangeType) GetMembers() map[string]MemberResolver { + r.initializeMemberResolvers() + return r.memberResolvers +} + +func InclusiveRangeContainsFunctionType(elementType Type) *FunctionType { + return &FunctionType{ + Parameters: []Parameter{ + { + Label: ArgumentLabelNotRequired, + Identifier: "element", + TypeAnnotation: NewTypeAnnotation(elementType), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + BoolType, + ), + } +} + +func (r *InclusiveRangeType) initializeMemberResolvers() { + r.memberResolversOnce.Do(func() { + r.memberResolvers = withBuiltinMembers(r, map[string]MemberResolver{ + InclusiveRangeTypeCountFieldName: { + Kind: common.DeclarationKindField, + Resolve: func(memoryGauge common.MemoryGauge, identifier string, _ ast.Range, _ func(error)) *Member { + return NewPublicConstantFieldMember( + memoryGauge, + r, + identifier, + r.ElementType(false), + inclusiveRangeTypeCountFieldDocString, + ) + }, + }, + InclusiveRangeTypeContainsFunctionName: { + Kind: common.DeclarationKindFunction, + Resolve: func(memoryGauge common.MemoryGauge, identifier string, targetRange ast.Range, report func(error)) *Member { + elementType := r.ElementType(false) + + return NewPublicFunctionMember( + memoryGauge, + r, + identifier, + InclusiveRangeContainsFunctionType(elementType), + inclusiveRangeTypeContainsFunctionDocString, + ) + }, + }, + }) + }) +} + +func (r *InclusiveRangeType) ElementType(_ bool) Type { + return r.MemberType +} + +func (*InclusiveRangeType) AllowsValueIndexingAssignment() bool { + return false +} + +func (r *InclusiveRangeType) Unify( + other Type, + typeParameters *TypeParameterTypeOrderedMap, + report func(err error), + outerRange ast.Range, +) bool { + otherRange, ok := other.(*InclusiveRangeType) + if !ok { + return false + } + + return r.MemberType.Unify(otherRange.MemberType, typeParameters, report, outerRange) +} + +func (r *InclusiveRangeType) Resolve(typeArguments *TypeParameterTypeOrderedMap) Type { + memberType := r.MemberType.Resolve(typeArguments) + if memberType == nil { + return nil + } + + return &InclusiveRangeType{ + MemberType: memberType, + } +} + // ReferenceType represents the reference to a value type ReferenceType struct { Type Type diff --git a/runtime/sema/type_tags.go b/runtime/sema/type_tags.go index 436c6f921e..73d16dde0f 100644 --- a/runtime/sema/type_tags.go +++ b/runtime/sema/type_tags.go @@ -224,6 +224,8 @@ const ( interfaceTypeMask functionTypeMask + inclusiveRangeTypeMask + invalidTypeMask ) @@ -327,14 +329,15 @@ var ( Or(CapabilityPathTypeTag). Or(StoragePathTypeTag) - ConstantSizedTypeTag = newTypeTagFromLowerMask(constantSizedTypeMask) - VariableSizedTypeTag = newTypeTagFromLowerMask(variableSizedTypeMask) - DictionaryTypeTag = newTypeTagFromLowerMask(dictionaryTypeMask) - CompositeTypeTag = newTypeTagFromLowerMask(compositeTypeMask) - ReferenceTypeTag = newTypeTagFromLowerMask(referenceTypeMask) - GenericTypeTag = newTypeTagFromLowerMask(genericTypeMask) - FunctionTypeTag = newTypeTagFromUpperMask(functionTypeMask) - InterfaceTypeTag = newTypeTagFromUpperMask(interfaceTypeMask) + ConstantSizedTypeTag = newTypeTagFromLowerMask(constantSizedTypeMask) + VariableSizedTypeTag = newTypeTagFromLowerMask(variableSizedTypeMask) + DictionaryTypeTag = newTypeTagFromLowerMask(dictionaryTypeMask) + InclusiveRangeTypeTag = newTypeTagFromUpperMask(inclusiveRangeTypeMask) + CompositeTypeTag = newTypeTagFromLowerMask(compositeTypeMask) + ReferenceTypeTag = newTypeTagFromLowerMask(referenceTypeMask) + GenericTypeTag = newTypeTagFromLowerMask(genericTypeMask) + FunctionTypeTag = newTypeTagFromUpperMask(functionTypeMask) + InterfaceTypeTag = newTypeTagFromUpperMask(interfaceTypeMask) RestrictedTypeTag = newTypeTagFromUpperMask(restrictedTypeMask) CapabilityTypeTag = newTypeTagFromUpperMask(capabilityTypeMask) @@ -673,6 +676,9 @@ func findSuperTypeFromUpperMask(joinedTypeTag TypeTag, types []Type) Type { functionTypeMask: return getSuperTypeOfDerivedTypes(types) + case inclusiveRangeTypeMask: + return commonSuperTypeOfRanges(types) + case anyResourceAttachmentMask: return AnyResourceAttachmentType @@ -690,6 +696,34 @@ func findSuperTypeFromUpperMask(joinedTypeTag TypeTag, types []Type) Type { } } +func commonSuperTypeOfRanges(types []Type) Type { + // We reach here if all types are range types. + // Therefore, decide the common supertype based on the member types. + + var memberTypes []Type + + for _, typ := range types { + // 'Never' type doesn't affect the supertype. + // Hence, ignore them + if typ == NeverType { + continue + } + + rangeType, ok := typ.(*InclusiveRangeType) + if !ok { + panic(errors.NewUnexpectedError("expected inclusive range type, found %s", typ)) + } + + memberTypes = append(memberTypes, rangeType.MemberType) + } + + memberSuperType := leastCommonSuperType(memberTypes...) + + return &InclusiveRangeType{ + MemberType: memberSuperType, + } +} + func getSuperTypeOfDerivedTypes(types []Type) Type { // We reach here if all types belongs to same kind. // e.g: All are arrays, all are dictionaries, etc. diff --git a/runtime/stdlib/builtin.go b/runtime/stdlib/builtin.go index 60e0111fc2..e5801a47b5 100644 --- a/runtime/stdlib/builtin.go +++ b/runtime/stdlib/builtin.go @@ -39,6 +39,7 @@ func DefaultStandardLibraryValues(handler StandardLibraryHandler) []StandardLibr PanicFunction, SignatureAlgorithmConstructor, RLPContract, + InclusiveRangeConstructorFunction, NewLogFunction(handler), NewUnsafeRandomFunction(handler), NewGetBlockFunction(handler), diff --git a/runtime/stdlib/range.go b/runtime/stdlib/range.go new file mode 100644 index 0000000000..94f510563f --- /dev/null +++ b/runtime/stdlib/range.go @@ -0,0 +1,133 @@ +/* + * Cadence - The resource-oriented smart contract programming language + * + * Copyright Dapper Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package stdlib + +import ( + "fmt" + + "github.com/onflow/cadence/runtime/errors" + "github.com/onflow/cadence/runtime/interpreter" + "github.com/onflow/cadence/runtime/sema" +) + +// InclusiveRangeConstructorFunction + +const inclusiveRangeConstructorFunctionDocString = ` + Constructs a Range covering from start to end. + + The step argument is optional and determines the step size. + If not provided, the value of +1 or -1 is used based on the values of start and end. + ` + +var inclusiveRangeConstructorFunctionType = func() *sema.FunctionType { + typeParameter := &sema.TypeParameter{ + Name: "T", + TypeBound: sema.IntegerType, + } + + typeAnnotation := sema.NewTypeAnnotation( + &sema.GenericType{ + TypeParameter: typeParameter, + }, + ) + + return &sema.FunctionType{ + TypeParameters: []*sema.TypeParameter{ + typeParameter, + }, + Parameters: []sema.Parameter{ + { + Label: sema.ArgumentLabelNotRequired, + Identifier: "start", + TypeAnnotation: typeAnnotation, + }, + { + Label: sema.ArgumentLabelNotRequired, + Identifier: "endInclusive", + TypeAnnotation: typeAnnotation, + }, + { + Identifier: "step", + TypeAnnotation: typeAnnotation, + }, + }, + ReturnTypeAnnotation: sema.NewTypeAnnotation( + &sema.InclusiveRangeType{ + MemberType: typeAnnotation.Type, + }, + ), + RequiredArgumentCount: sema.RequiredArgumentCount(2), + } +}() + +var InclusiveRangeConstructorFunction = NewStandardLibraryFunction( + "InclusiveRange", + inclusiveRangeConstructorFunctionType, + inclusiveRangeConstructorFunctionDocString, + func(invocation interpreter.Invocation) interpreter.Value { + start, startOk := invocation.Arguments[0].(interpreter.IntegerValue) + endInclusive, endInclusiveOk := invocation.Arguments[1].(interpreter.IntegerValue) + + if !startOk || !endInclusiveOk { + panic(errors.NewUnreachableError()) + } + + inter := invocation.Interpreter + locationRange := invocation.LocationRange + + leftStaticType := start.StaticType(inter) + rightStaticType := endInclusive.StaticType(inter) + if leftStaticType != rightStaticType { + // Checker would only allow same type for both start & endInclusive. + panic(errors.NewUnreachableError()) + } + + rangeStaticType := interpreter.InclusiveRangeStaticType{ElementType: leftStaticType} + + if len(invocation.Arguments) > 2 { + step, ok := invocation.Arguments[2].(interpreter.IntegerValue) + if !ok { + panic(errors.NewUnreachableError()) + } + + return interpreter.NewInclusiveRangeValueWithStep(inter, locationRange, start, endInclusive, step, rangeStaticType) + } else { + return interpreter.NewInclusiveRangeValue(inter, locationRange, start, endInclusive, rangeStaticType) + } + }, +) + +// InclusiveRangeConstructionError + +type InclusiveRangeConstructionError struct { + interpreter.LocationRange + Message string +} + +var _ errors.UserError = InclusiveRangeConstructionError{} + +func (InclusiveRangeConstructionError) IsUserError() {} + +func (e InclusiveRangeConstructionError) Error() string { + const message = "InclusiveRange construction failed" + if e.Message == "" { + return message + } + return fmt.Sprintf("%s: %s", message, e.Message) +} diff --git a/runtime/tests/checker/range_value_test.go b/runtime/tests/checker/range_value_test.go new file mode 100644 index 0000000000..863be95685 --- /dev/null +++ b/runtime/tests/checker/range_value_test.go @@ -0,0 +1,96 @@ +/* + * Cadence - The resource-oriented smart contract programming language + * + * Copyright Dapper Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package checker + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/onflow/cadence/runtime/sema" + "github.com/onflow/cadence/runtime/stdlib" +) + +func TestInclusiveRange(t *testing.T) { + t.Parallel() + + baseValueActivation := sema.NewVariableActivation(sema.BaseValueActivation) + baseValueActivation.DeclareValue(stdlib.InclusiveRangeConstructorFunction) + + runValidCase := func(t *testing.T, memberType sema.Type, withStep bool) { + t.Run(memberType.String(), func(t *testing.T) { + t.Parallel() + + var code string + if withStep { + code = fmt.Sprintf( + ` + let s : %s = 10 + let e : %s = 20 + let step : %s = 2 + let r = InclusiveRange(s, e, step: step) + `, + memberType.String(), memberType.String(), memberType.String()) + } else { + code = fmt.Sprintf( + ` + let s : %s = 10 + let e : %s = 20 + let r = InclusiveRange(s, e) + `, + memberType.String(), memberType.String()) + } + + checker, err := ParseAndCheckWithOptions(t, code, + ParseAndCheckOptions{ + Config: &sema.Config{ + BaseValueActivation: baseValueActivation, + }, + }, + ) + + require.NoError(t, err) + resType := RequireGlobalValue(t, checker.Elaboration, "r") + require.Equal(t, + &sema.InclusiveRangeType{ + MemberType: memberType, + }, + resType, + ) + }) + } + + runValidCaseWithoutStep := func(t *testing.T, memberType sema.Type) { + runValidCase(t, memberType, false) + } + runValidCaseWithStep := func(t *testing.T, memberType sema.Type) { + runValidCase(t, memberType, true) + } + + for _, integerType := range sema.AllIntegerTypes { + switch integerType { + case sema.IntegerType, sema.SignedIntegerType: + continue + } + + runValidCaseWithStep(t, integerType) + runValidCaseWithoutStep(t, integerType) + } +} diff --git a/runtime/tests/interpreter/range_value_test.go b/runtime/tests/interpreter/range_value_test.go new file mode 100644 index 0000000000..4d873d1457 --- /dev/null +++ b/runtime/tests/interpreter/range_value_test.go @@ -0,0 +1,105 @@ +/* + * Cadence - The resource-oriented smart contract programming language + * + * Copyright Dapper Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package interpreter_test + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/onflow/cadence/runtime/activations" + "github.com/onflow/cadence/runtime/interpreter" + "github.com/onflow/cadence/runtime/sema" + "github.com/onflow/cadence/runtime/stdlib" +) + +func TestInclusiveRange(t *testing.T) { + t.Parallel() + + baseValueActivation := sema.NewVariableActivation(sema.BaseValueActivation) + baseValueActivation.DeclareValue(stdlib.InclusiveRangeConstructorFunction) + + baseActivation := activations.NewActivation(nil, interpreter.BaseActivation) + interpreter.Declare(baseActivation, stdlib.InclusiveRangeConstructorFunction) + + runValidCase := func(t *testing.T, memberType sema.Type, withStep bool) { + t.Run(memberType.String(), func(t *testing.T) { + t.Parallel() + + var code string + if withStep { + code = fmt.Sprintf( + ` + let s : %s = 10 + let e : %s = 20 + let step : %s = 2 + let r = InclusiveRange(s, e, step: step) + + let count = r.count + + let containsTest = r.contains(s) + `, + memberType.String(), memberType.String(), memberType.String()) + } else { + code = fmt.Sprintf( + ` + let s : %s = 10 + let e : %s = 20 + let r = InclusiveRange(s, e) + + let count = r.count + + let containsTest = r.contains(s) + `, + memberType.String(), memberType.String()) + } + + _, err := parseCheckAndInterpretWithOptions(t, code, + ParseCheckAndInterpretOptions{ + CheckerConfig: &sema.Config{ + BaseValueActivation: baseValueActivation, + }, + Config: &interpreter.Config{ + BaseActivation: baseActivation, + }, + }, + ) + + require.NoError(t, err) + }) + } + + runValidCaseWithoutStep := func(t *testing.T, memberType sema.Type) { + runValidCase(t, memberType, false) + } + runValidCaseWithStep := func(t *testing.T, memberType sema.Type) { + runValidCase(t, memberType, true) + } + + for _, integerType := range sema.AllIntegerTypes { + switch integerType { + case sema.IntegerType, sema.SignedIntegerType: + continue + } + + runValidCaseWithStep(t, integerType) + runValidCaseWithoutStep(t, integerType) + } +} diff --git a/types.go b/types.go index 9c735e8dbc..f6f2059a18 100644 --- a/types.go +++ b/types.go @@ -1059,6 +1059,52 @@ func (t *DictionaryType) Equal(other Type) bool { t.ElementType.Equal(otherType.ElementType) } +// InclusiveRangeType + +type InclusiveRangeType struct { + ElementType Type + typeID string +} + +var _ Type = &InclusiveRangeType{} + +func NewInclusiveRangeType( + elementType Type, +) *InclusiveRangeType { + return &InclusiveRangeType{ + ElementType: elementType, + } +} + +func NewMeteredInclusiveRangeType( + gauge common.MemoryGauge, + elementType Type, +) *InclusiveRangeType { + common.UseMemory(gauge, common.CadenceInclusiveRangeTypeMemoryUsage) + return NewInclusiveRangeType(elementType) +} + +func (*InclusiveRangeType) isType() {} + +func (t *InclusiveRangeType) ID() string { + if len(t.typeID) == 0 { + t.typeID = fmt.Sprintf( + "InclusiveRange<%s>", + t.ElementType.ID(), + ) + } + return t.typeID +} + +func (t *InclusiveRangeType) Equal(other Type) bool { + otherType, ok := other.(*InclusiveRangeType) + if !ok { + return false + } + + return t.ElementType.Equal(otherType.ElementType) +} + // Field type Field struct { From 9517f7e8baa4c9ad89be0c4534a8918f52eb9ab8 Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Tue, 20 Jun 2023 01:00:49 +0530 Subject: [PATCH 002/121] Validate that the step is always non-zero --- runtime/interpreter/errors.go | 19 +++++++++++++++++++ runtime/interpreter/value_range.go | 9 ++++++++- runtime/stdlib/range.go | 21 --------------------- 3 files changed, 27 insertions(+), 22 deletions(-) diff --git a/runtime/interpreter/errors.go b/runtime/interpreter/errors.go index 5b57ea8fe6..c0f82997db 100644 --- a/runtime/interpreter/errors.go +++ b/runtime/interpreter/errors.go @@ -1008,3 +1008,22 @@ func WrappedExternalError(err error) error { return errors.NewExternalError(err) } } + +// InclusiveRangeConstructionError + +type InclusiveRangeConstructionError struct { + LocationRange + Message string +} + +var _ errors.UserError = InclusiveRangeConstructionError{} + +func (InclusiveRangeConstructionError) IsUserError() {} + +func (e InclusiveRangeConstructionError) Error() string { + const message = "InclusiveRange construction failed" + if e.Message == "" { + return message + } + return fmt.Sprintf("%s: %s", message, e.Message) +} diff --git a/runtime/interpreter/value_range.go b/runtime/interpreter/value_range.go index ba80a8983f..8b65432839 100644 --- a/runtime/interpreter/value_range.go +++ b/runtime/interpreter/value_range.go @@ -62,7 +62,14 @@ func NewInclusiveRangeValueWithStep( ) *CompositeValue { // TODO: Validate if the sequence is moving away from the end value. - // Also validate that step is non-zero. + + // Validate that the step is non-zero. + if step.Equal(interpreter, locationRange, getValueForIntegerType(0, rangeType.ElementType)) { + panic(InclusiveRangeConstructionError{ + LocationRange: locationRange, + Message: "step value cannot be zero", + }) + } fields := []CompositeField{ { diff --git a/runtime/stdlib/range.go b/runtime/stdlib/range.go index 94f510563f..7d12f946fb 100644 --- a/runtime/stdlib/range.go +++ b/runtime/stdlib/range.go @@ -19,8 +19,6 @@ package stdlib import ( - "fmt" - "github.com/onflow/cadence/runtime/errors" "github.com/onflow/cadence/runtime/interpreter" "github.com/onflow/cadence/runtime/sema" @@ -112,22 +110,3 @@ var InclusiveRangeConstructorFunction = NewStandardLibraryFunction( } }, ) - -// InclusiveRangeConstructionError - -type InclusiveRangeConstructionError struct { - interpreter.LocationRange - Message string -} - -var _ errors.UserError = InclusiveRangeConstructionError{} - -func (InclusiveRangeConstructionError) IsUserError() {} - -func (e InclusiveRangeConstructionError) Error() string { - const message = "InclusiveRange construction failed" - if e.Message == "" { - return message - } - return fmt.Sprintf("%s: %s", message, e.Message) -} From f48199c79d96937f8890e06de1e21be84b4322cb Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Tue, 20 Jun 2023 01:24:45 +0530 Subject: [PATCH 003/121] Validate that step takes the direction of sequence towards end --- runtime/interpreter/value_range.go | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/runtime/interpreter/value_range.go b/runtime/interpreter/value_range.go index 8b65432839..4b65cc9ca7 100644 --- a/runtime/interpreter/value_range.go +++ b/runtime/interpreter/value_range.go @@ -19,6 +19,8 @@ package interpreter import ( + "fmt" + "github.com/onflow/cadence/runtime/common" "github.com/onflow/cadence/runtime/errors" "github.com/onflow/cadence/runtime/sema" @@ -61,8 +63,6 @@ func NewInclusiveRangeValueWithStep( rangeType InclusiveRangeStaticType, ) *CompositeValue { - // TODO: Validate if the sequence is moving away from the end value. - // Validate that the step is non-zero. if step.Equal(interpreter, locationRange, getValueForIntegerType(0, rangeType.ElementType)) { panic(InclusiveRangeConstructionError{ @@ -71,6 +71,19 @@ func NewInclusiveRangeValueWithStep( }) } + // Validate that the sequence is moving towards the end value. + // If start < end, step must be > 0 + // If start > end, step must be < 0 + // If start == end, step doesn't matter. + if (start.Less(interpreter, end, locationRange) && step.Less(interpreter, getValueForIntegerType(0, rangeType.ElementType), locationRange)) || + (start.Greater(interpreter, end, locationRange) && step.Greater(interpreter, getValueForIntegerType(0, rangeType.ElementType), locationRange)) { + + panic(InclusiveRangeConstructionError{ + LocationRange: locationRange, + Message: fmt.Sprintf("sequence is moving away from end: %s due to the value of step: %s and start: %s", end, step, start), + }) + } + fields := []CompositeField{ { Name: sema.InclusiveRangeTypeStartFieldName, From d13a39ce263152311837023870f189bc7cb1721c Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Wed, 21 Jun 2023 22:12:36 +0530 Subject: [PATCH 004/121] More tests --- runtime/interpreter/value.go | 2 +- runtime/interpreter/value_range.go | 17 +- runtime/sema/type.go | 43 +++- runtime/tests/checker/range_value_test.go | 245 +++++++++++++++++++--- 4 files changed, 252 insertions(+), 55 deletions(-) diff --git a/runtime/interpreter/value.go b/runtime/interpreter/value.go index aef3904c73..20b63720b3 100644 --- a/runtime/interpreter/value.go +++ b/runtime/interpreter/value.go @@ -16286,7 +16286,7 @@ func NewUnmeteredCompositeField(name string, value Value) CompositeField { // Create a CompositeValue with the provided StaticType. // Useful when we wish to utilize CompositeValue as the value // for a type which isn't CompositeType. -// For e.g. RangeType +// For e.g. InclusiveRangeType func NewCompositeValueWithStaticType( interpreter *Interpreter, locationRange LocationRange, diff --git a/runtime/interpreter/value_range.go b/runtime/interpreter/value_range.go index 4b65cc9ca7..df14ed4886 100644 --- a/runtime/interpreter/value_range.go +++ b/runtime/interpreter/value_range.go @@ -90,7 +90,7 @@ func NewInclusiveRangeValueWithStep( Value: start, }, { - Name: sema.InclusiveRangeTypeEndInclusiveFieldName, + Name: sema.InclusiveRangeTypeEndFieldName, Value: end, }, { @@ -112,19 +112,6 @@ func NewInclusiveRangeValueWithStep( rangeType, ) - rangeValue.ComputedFields = map[string]ComputedField{ - sema.InclusiveRangeTypeCountFieldName: func(interpreter *Interpreter, locationRange LocationRange) Value { - start := getFieldAsIntegerValue(rangeValue, interpreter, locationRange, sema.InclusiveRangeTypeStartFieldName) - endInclusive := getFieldAsIntegerValue(rangeValue, interpreter, locationRange, sema.InclusiveRangeTypeEndInclusiveFieldName) - step := getFieldAsIntegerValue(rangeValue, interpreter, locationRange, sema.InclusiveRangeTypeStepFieldName) - - diff := convertAndAssertIntegerValue(endInclusive.Minus(interpreter, start, locationRange)) - - // Perform integer division & drop the decimal part. - // Note that step is guaranteed to be non-zero. - return diff.Div(interpreter, step, locationRange) - }, - } rangeValue.Functions = map[string]FunctionValue{ sema.InclusiveRangeTypeContainsFunctionName: NewHostFunctionValue( interpreter, @@ -158,7 +145,7 @@ func rangeContains( needleValue Value, ) BoolValue { start := getFieldAsIntegerValue(rangeValue, interpreter, locationRange, sema.InclusiveRangeTypeStartFieldName) - endInclusive := getFieldAsIntegerValue(rangeValue, interpreter, locationRange, sema.InclusiveRangeTypeEndInclusiveFieldName) + endInclusive := getFieldAsIntegerValue(rangeValue, interpreter, locationRange, sema.InclusiveRangeTypeEndFieldName) step := getFieldAsIntegerValue(rangeValue, interpreter, locationRange, sema.InclusiveRangeTypeStepFieldName) needleInteger := convertAndAssertIntegerValue(needleValue) diff --git a/runtime/sema/type.go b/runtime/sema/type.go index 02e52bb735..7bb45a310c 100644 --- a/runtime/sema/type.go +++ b/runtime/sema/type.go @@ -5324,12 +5324,17 @@ func (r *InclusiveRangeType) RewriteWithRestrictedTypes() (Type, bool) { } const InclusiveRangeTypeStartFieldName = "start" -const InclusiveRangeTypeEndInclusiveFieldName = "endInclusive" -const InclusiveRangeTypeStepFieldName = "step" -const InclusiveRangeTypeCountFieldName = "count" +const inclusiveRangeTypeStartFieldDocString = ` +The start of the InclusiveRange sequence +` +const InclusiveRangeTypeEndFieldName = "end" +const inclusiveRangeTypeEndFieldDocString = ` +The end of the InclusiveRange sequence +` -const inclusiveRangeTypeCountFieldDocString = ` -The number of entries in the Range sequence +const InclusiveRangeTypeStepFieldName = "step" +const inclusiveRangeTypeStepFieldDocString = ` +The step size of the InclusiveRange sequence ` const InclusiveRangeTypeContainsFunctionName = "contains" @@ -5361,7 +5366,31 @@ func InclusiveRangeContainsFunctionType(elementType Type) *FunctionType { func (r *InclusiveRangeType) initializeMemberResolvers() { r.memberResolversOnce.Do(func() { r.memberResolvers = withBuiltinMembers(r, map[string]MemberResolver{ - InclusiveRangeTypeCountFieldName: { + InclusiveRangeTypeStartFieldName: { + Kind: common.DeclarationKindField, + Resolve: func(memoryGauge common.MemoryGauge, identifier string, _ ast.Range, _ func(error)) *Member { + return NewPublicConstantFieldMember( + memoryGauge, + r, + identifier, + r.ElementType(false), + inclusiveRangeTypeStartFieldDocString, + ) + }, + }, + InclusiveRangeTypeEndFieldName: { + Kind: common.DeclarationKindField, + Resolve: func(memoryGauge common.MemoryGauge, identifier string, _ ast.Range, _ func(error)) *Member { + return NewPublicConstantFieldMember( + memoryGauge, + r, + identifier, + r.ElementType(false), + inclusiveRangeTypeEndFieldDocString, + ) + }, + }, + InclusiveRangeTypeStepFieldName: { Kind: common.DeclarationKindField, Resolve: func(memoryGauge common.MemoryGauge, identifier string, _ ast.Range, _ func(error)) *Member { return NewPublicConstantFieldMember( @@ -5369,7 +5398,7 @@ func (r *InclusiveRangeType) initializeMemberResolvers() { r, identifier, r.ElementType(false), - inclusiveRangeTypeCountFieldDocString, + inclusiveRangeTypeStepFieldDocString, ) }, }, diff --git a/runtime/tests/checker/range_value_test.go b/runtime/tests/checker/range_value_test.go index 863be95685..b6af07f571 100644 --- a/runtime/tests/checker/range_value_test.go +++ b/runtime/tests/checker/range_value_test.go @@ -22,40 +22,226 @@ import ( "fmt" "testing" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/onflow/cadence/runtime/sema" "github.com/onflow/cadence/runtime/stdlib" ) -func TestInclusiveRange(t *testing.T) { +type inclusiveRangeConstructionTest struct { + ty sema.Type + s, e, step int64 +} + +func TestInclusiveRangeConstruction(t *testing.T) { t.Parallel() baseValueActivation := sema.NewVariableActivation(sema.BaseValueActivation) baseValueActivation.DeclareValue(stdlib.InclusiveRangeConstructorFunction) - runValidCase := func(t *testing.T, memberType sema.Type, withStep bool) { - t.Run(memberType.String(), func(t *testing.T) { + validTestCases := []inclusiveRangeConstructionTest{ + // Int* + { + ty: sema.IntType, + s: 0, + e: 10, + step: 2, + }, + { + ty: sema.IntType, + s: 10, + e: -10, + step: -2, + }, + { + ty: sema.Int8Type, + s: 0, + e: 10, + step: 2, + }, + { + ty: sema.Int8Type, + s: 10, + e: -10, + step: -2, + }, + { + ty: sema.Int16Type, + s: 0, + e: 10, + step: 2, + }, + { + ty: sema.Int16Type, + s: 10, + e: -10, + step: -2, + }, + { + ty: sema.Int32Type, + s: 0, + e: 10, + step: 2, + }, + { + ty: sema.Int32Type, + s: 10, + e: -10, + step: -2, + }, + { + ty: sema.Int64Type, + s: 0, + e: 10, + step: 2, + }, + { + ty: sema.Int64Type, + s: 10, + e: -10, + step: -2, + }, + { + ty: sema.Int128Type, + s: 0, + e: 10, + step: 2, + }, + { + ty: sema.Int128Type, + s: 10, + e: -10, + step: -2, + }, + { + ty: sema.Int256Type, + s: 0, + e: 10, + step: 2, + }, + { + ty: sema.Int256Type, + s: 10, + e: -10, + step: -2, + }, + + // UInt* + { + ty: sema.UIntType, + s: 0, + e: 10, + step: 2, + }, + { + ty: sema.UInt8Type, + s: 0, + e: 10, + step: 2, + }, + { + ty: sema.UInt16Type, + s: 0, + e: 10, + step: 2, + }, + { + ty: sema.UInt32Type, + s: 0, + e: 10, + step: 2, + }, + { + ty: sema.UInt64Type, + s: 0, + e: 10, + step: 2, + }, + { + ty: sema.UInt128Type, + s: 0, + e: 10, + step: 2, + }, + { + ty: sema.UInt256Type, + s: 0, + e: 10, + step: 2, + }, + + // Word* + { + ty: sema.Word8Type, + s: 0, + e: 10, + step: 2, + }, + { + ty: sema.Word16Type, + s: 0, + e: 10, + step: 2, + }, + { + ty: sema.Word32Type, + s: 0, + e: 10, + step: 2, + }, + { + ty: sema.Word64Type, + s: 0, + e: 10, + step: 2, + }, + { + ty: sema.Word128Type, + s: 0, + e: 10, + step: 2, + }, + { + ty: sema.Word256Type, + s: 0, + e: 10, + step: 2, + }, + } + + runValidCase := func(t *testing.T, testCase inclusiveRangeConstructionTest, withStep bool) { + t.Run(testCase.ty.String(), func(t *testing.T) { t.Parallel() var code string if withStep { code = fmt.Sprintf( ` - let s : %s = 10 - let e : %s = 20 - let step : %s = 2 + let s : %s = %d + let e : %s = %d + let step : %s = %d let r = InclusiveRange(s, e, step: step) + + let rs = r.start + let re = r.end + let rstep = r.step + let contains_res = r.contains(s) `, - memberType.String(), memberType.String(), memberType.String()) + testCase.ty.String(), testCase.s, testCase.ty.String(), testCase.e, testCase.ty.String(), testCase.step) } else { code = fmt.Sprintf( ` - let s : %s = 10 - let e : %s = 20 + let s : %s = %d + let e : %s = %d let r = InclusiveRange(s, e) + + let rs = r.start + let re = r.end + let rstep = r.step + let contains_res = r.contains(s) `, - memberType.String(), memberType.String()) + testCase.ty.String(), testCase.s, testCase.ty.String(), testCase.e) } checker, err := ParseAndCheckWithOptions(t, code, @@ -67,30 +253,25 @@ func TestInclusiveRange(t *testing.T) { ) require.NoError(t, err) - resType := RequireGlobalValue(t, checker.Elaboration, "r") - require.Equal(t, - &sema.InclusiveRangeType{ - MemberType: memberType, - }, - resType, - ) - }) - } - runValidCaseWithoutStep := func(t *testing.T, memberType sema.Type) { - runValidCase(t, memberType, false) - } - runValidCaseWithStep := func(t *testing.T, memberType sema.Type) { - runValidCase(t, memberType, true) - } + checkType := func(t *testing.T, name string, expectedType sema.Type) { + resType := RequireGlobalValue(t, checker.Elaboration, name) + assert.IsType(t, expectedType, resType) + } - for _, integerType := range sema.AllIntegerTypes { - switch integerType { - case sema.IntegerType, sema.SignedIntegerType: - continue - } + checkType(t, "r", &sema.InclusiveRangeType{ + MemberType: testCase.ty, + }) + checkType(t, "rs", testCase.ty) + checkType(t, "re", testCase.ty) + checkType(t, "rstep", testCase.ty) + checkType(t, "contains_res", sema.BoolType) + }) + } - runValidCaseWithStep(t, integerType) - runValidCaseWithoutStep(t, integerType) + // Run each test case with and without step. + for _, testCase := range validTestCases { + runValidCase(t, testCase, true) + runValidCase(t, testCase, false) } } From 258e54113bb33193f6a9042eadbe173627a3bc5c Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Mon, 26 Jun 2023 01:07:26 +0530 Subject: [PATCH 005/121] Add invalid case tests in checker --- runtime/interpreter/value_range.go | 8 +- runtime/tests/checker/range_value_test.go | 90 ++++++++++++++++++++++- 2 files changed, 95 insertions(+), 3 deletions(-) diff --git a/runtime/interpreter/value_range.go b/runtime/interpreter/value_range.go index df14ed4886..41a8859adc 100644 --- a/runtime/interpreter/value_range.go +++ b/runtime/interpreter/value_range.go @@ -26,7 +26,7 @@ import ( "github.com/onflow/cadence/runtime/sema" ) -// NewInclusiveRangeValue constructs a InclusiveRange value with the provided start, end with default value of step. +// NewInclusiveRangeValue constructs an InclusiveRange value with the provided start, end with default value of step. func NewInclusiveRangeValue( interpreter *Interpreter, locationRange LocationRange, @@ -42,6 +42,8 @@ func NewInclusiveRangeValue( step := getValueForIntegerType(1, rangeType.ElementType) if startComparable.Greater(interpreter, endInclusiveComparable, locationRange) { + // TODO: Disallow unsigned integers to have a negative step. + negatedStep, ok := step.Negate(interpreter, locationRange).(IntegerValue) if !ok { panic(errors.NewUnreachableError()) @@ -53,7 +55,7 @@ func NewInclusiveRangeValue( return NewInclusiveRangeValueWithStep(interpreter, locationRange, start, end, step, rangeType) } -// NewInclusiveRangeValue constructs a InclusiveRange value with the provided start, end & step. +// NewInclusiveRangeValue constructs an InclusiveRange value with the provided start, end & step. func NewInclusiveRangeValueWithStep( interpreter *Interpreter, locationRange LocationRange, @@ -63,6 +65,8 @@ func NewInclusiveRangeValueWithStep( rangeType InclusiveRangeStaticType, ) *CompositeValue { + // TODO: Validate that if start > end, then the type is signed integer. + // Validate that the step is non-zero. if step.Equal(interpreter, locationRange, getValueForIntegerType(0, rangeType.ElementType)) { panic(InclusiveRangeConstructionError{ diff --git a/runtime/tests/checker/range_value_test.go b/runtime/tests/checker/range_value_test.go index b6af07f571..8adfd7135f 100644 --- a/runtime/tests/checker/range_value_test.go +++ b/runtime/tests/checker/range_value_test.go @@ -34,7 +34,7 @@ type inclusiveRangeConstructionTest struct { s, e, step int64 } -func TestInclusiveRangeConstruction(t *testing.T) { +func TestInclusiveRangeConstructionValid(t *testing.T) { t.Parallel() baseValueActivation := sema.NewVariableActivation(sema.BaseValueActivation) @@ -275,3 +275,91 @@ func TestInclusiveRangeConstruction(t *testing.T) { runValidCase(t, testCase, false) } } + +func TestInclusiveRangeConstructionInvalid(t *testing.T) { + // t.Parallel() + + baseValueActivation := sema.NewVariableActivation(sema.BaseValueActivation) + baseValueActivation.DeclareValue(stdlib.InclusiveRangeConstructorFunction) + + runInvalidCase := func(t *testing.T, label, code string, expectedErrorTypes []interface{}) { + t.Run(label, func(t *testing.T) { + // t.Parallel() + + _, err := ParseAndCheckWithOptions(t, code, + ParseAndCheckOptions{ + Config: &sema.Config{ + BaseValueActivation: baseValueActivation, + }, + }, + ) + + errs := RequireCheckerErrors(t, err, len(expectedErrorTypes)) + for i, err := range expectedErrorTypes { + assert.IsType(t, err, errs[i]) + } + }) + } + + for _, integerType := range sema.AllIntegerTypes { + // Only test leaf types + switch integerType { + case sema.IntegerType, sema.SignedIntegerType: + continue + } + + typeString := integerType.String() + + // Wrong type of arguments + + // Any integer type name other than the integerType is sufficient. + // There is nothing special about the Int128 and Int256 types here. + differentTypeString := sema.Int128TypeName + if typeString == differentTypeString { + differentTypeString = sema.Int256TypeName + } + + runInvalidCase( + t, + typeString, + fmt.Sprintf("let r = InclusiveRange(%s(1), %s(2))", typeString, differentTypeString), + []interface{}{&sema.TypeParameterTypeMismatchError{}, &sema.TypeMismatchError{}}, + ) + runInvalidCase( + t, + typeString, + fmt.Sprintf("let r = InclusiveRange(%s(1), %s(10), step: %s(2))", typeString, typeString, differentTypeString), + []interface{}{&sema.TypeParameterTypeMismatchError{}, &sema.TypeMismatchError{}}, + ) + + // Not enough arguments + runInvalidCase( + t, + typeString, + fmt.Sprintf("let r = InclusiveRange(%s(1))", typeString), + []interface{}{&sema.ArgumentCountError{}}, + ) + + // Label for step not provided + runInvalidCase( + t, + typeString, + fmt.Sprintf("let r = InclusiveRange(%s(1), %s(0), %s(10))", typeString, typeString, typeString), + []interface{}{&sema.MissingArgumentLabelError{}}, + ) + + // Label for start and end provided + runInvalidCase( + t, + typeString, + fmt.Sprintf("let r = InclusiveRange(start: %s(1), %s(0))", typeString, typeString), + []interface{}{&sema.IncorrectArgumentLabelError{}}, + ) + runInvalidCase( + t, + typeString, + fmt.Sprintf("let r = InclusiveRange(%s(1), end: %s(0))", typeString, typeString), + []interface{}{&sema.IncorrectArgumentLabelError{}}, + ) + } +} From 4fbe486474a29cd954004cc934efbbb9d5ad1792 Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Thu, 29 Jun 2023 22:18:18 +0530 Subject: [PATCH 006/121] Add interpreter test cases --- runtime/interpreter/value_range.go | 24 +- runtime/sema/type.go | 7 + runtime/tests/checker/range_value_test.go | 4 +- runtime/tests/interpreter/range_value_test.go | 504 +++++++++++++++++- 4 files changed, 505 insertions(+), 34 deletions(-) diff --git a/runtime/interpreter/value_range.go b/runtime/interpreter/value_range.go index 41a8859adc..0683ed6f32 100644 --- a/runtime/interpreter/value_range.go +++ b/runtime/interpreter/value_range.go @@ -40,9 +40,15 @@ func NewInclusiveRangeValue( panic(errors.NewUnreachableError()) } - step := getValueForIntegerType(1, rangeType.ElementType) + step := GetValueForIntegerType(1, rangeType.ElementType) if startComparable.Greater(interpreter, endInclusiveComparable, locationRange) { - // TODO: Disallow unsigned integers to have a negative step. + elemSemaTy := interpreter.MustConvertStaticToSemaType(rangeType.ElementType) + if _, ok := sema.AllUnsignedIntegerTypesSet[elemSemaTy]; ok { + panic(InclusiveRangeConstructionError{ + LocationRange: locationRange, + Message: fmt.Sprintf("step value cannot be negative for unsigned integer type %s", elemSemaTy), + }) + } negatedStep, ok := step.Negate(interpreter, locationRange).(IntegerValue) if !ok { @@ -65,10 +71,8 @@ func NewInclusiveRangeValueWithStep( rangeType InclusiveRangeStaticType, ) *CompositeValue { - // TODO: Validate that if start > end, then the type is signed integer. - // Validate that the step is non-zero. - if step.Equal(interpreter, locationRange, getValueForIntegerType(0, rangeType.ElementType)) { + if step.Equal(interpreter, locationRange, GetValueForIntegerType(0, rangeType.ElementType)) { panic(InclusiveRangeConstructionError{ LocationRange: locationRange, Message: "step value cannot be zero", @@ -79,8 +83,8 @@ func NewInclusiveRangeValueWithStep( // If start < end, step must be > 0 // If start > end, step must be < 0 // If start == end, step doesn't matter. - if (start.Less(interpreter, end, locationRange) && step.Less(interpreter, getValueForIntegerType(0, rangeType.ElementType), locationRange)) || - (start.Greater(interpreter, end, locationRange) && step.Greater(interpreter, getValueForIntegerType(0, rangeType.ElementType), locationRange)) { + if (start.Less(interpreter, end, locationRange) && step.Less(interpreter, GetValueForIntegerType(0, rangeType.ElementType), locationRange)) || + (start.Greater(interpreter, end, locationRange) && step.Greater(interpreter, GetValueForIntegerType(0, rangeType.ElementType), locationRange)) { panic(InclusiveRangeConstructionError{ LocationRange: locationRange, @@ -173,7 +177,7 @@ func rangeContains( panic(errors.NewUnreachableError()) } - result = diff.Mod(interpreter, step, locationRange).Equal(interpreter, locationRange, getValueForIntegerType(0, rangeType.ElementType)) + result = diff.Mod(interpreter, step, locationRange).Equal(interpreter, locationRange, GetValueForIntegerType(0, rangeType.ElementType)) } } @@ -182,7 +186,7 @@ func rangeContains( // Get the provided int64 value in the required staticType. // Note: Assumes that the provided value fits within the constraints of the staticType. -func getValueForIntegerType(value int64, staticType StaticType) IntegerValue { +func GetValueForIntegerType(value int64, staticType StaticType) IntegerValue { switch staticType { case PrimitiveStaticTypeInt: return NewUnmeteredIntValueFromInt64(value) @@ -242,7 +246,7 @@ func getFieldAsIntegerValue( rangeValue.GetField( interpreter, locationRange, - sema.InclusiveRangeTypeStartFieldName, + name, ), ) } diff --git a/runtime/sema/type.go b/runtime/sema/type.go index 7bb45a310c..577a07cf60 100644 --- a/runtime/sema/type.go +++ b/runtime/sema/type.go @@ -3456,6 +3456,8 @@ var AllUnsignedIntegerTypes = []Type{ Word256Type, } +var AllUnsignedIntegerTypesSet = make(map[Type]struct{}) + var AllIntegerTypes = common.Concat( AllUnsignedIntegerTypes, AllSignedIntegerTypes, @@ -3603,6 +3605,11 @@ func init() { ) } } + + // Populate AllUnsignedIntegerTypesSet + for _, ty := range AllUnsignedIntegerTypes { + AllUnsignedIntegerTypesSet[ty] = struct{}{} + } } func NumberConversionFunctionType(numberType Type) *FunctionType { diff --git a/runtime/tests/checker/range_value_test.go b/runtime/tests/checker/range_value_test.go index 8adfd7135f..7f5be159da 100644 --- a/runtime/tests/checker/range_value_test.go +++ b/runtime/tests/checker/range_value_test.go @@ -277,14 +277,14 @@ func TestInclusiveRangeConstructionValid(t *testing.T) { } func TestInclusiveRangeConstructionInvalid(t *testing.T) { - // t.Parallel() + t.Parallel() baseValueActivation := sema.NewVariableActivation(sema.BaseValueActivation) baseValueActivation.DeclareValue(stdlib.InclusiveRangeConstructorFunction) runInvalidCase := func(t *testing.T, label, code string, expectedErrorTypes []interface{}) { t.Run(label, func(t *testing.T) { - // t.Parallel() + t.Parallel() _, err := ParseAndCheckWithOptions(t, code, ParseAndCheckOptions{ diff --git a/runtime/tests/interpreter/range_value_test.go b/runtime/tests/interpreter/range_value_test.go index 4d873d1457..cb17855eec 100644 --- a/runtime/tests/interpreter/range_value_test.go +++ b/runtime/tests/interpreter/range_value_test.go @@ -20,6 +20,7 @@ package interpreter_test import ( "fmt" + "strings" "testing" "github.com/stretchr/testify/require" @@ -28,8 +29,22 @@ import ( "github.com/onflow/cadence/runtime/interpreter" "github.com/onflow/cadence/runtime/sema" "github.com/onflow/cadence/runtime/stdlib" + "github.com/onflow/cadence/runtime/tests/utils" + . "github.com/onflow/cadence/runtime/tests/utils" ) +type containsTestCase struct { + param int64 + expectedWithoutStep bool + expectedWithStep bool +} + +type inclusiveRangeConstructionTest struct { + ty sema.Type + s, e, step int64 + containsTests []containsTestCase +} + func TestInclusiveRange(t *testing.T) { t.Parallel() @@ -39,39 +54,343 @@ func TestInclusiveRange(t *testing.T) { baseActivation := activations.NewActivation(nil, interpreter.BaseActivation) interpreter.Declare(baseActivation, stdlib.InclusiveRangeConstructorFunction) - runValidCase := func(t *testing.T, memberType sema.Type, withStep bool) { - t.Run(memberType.String(), func(t *testing.T) { + unsignedContainsTestCases := []containsTestCase{ + { + param: 1, + expectedWithoutStep: true, + expectedWithStep: false, + }, + { + param: 12, + expectedWithoutStep: false, + expectedWithStep: false, + }, + { + param: 10, + expectedWithoutStep: true, + expectedWithStep: true, + }, + { + param: 0, + expectedWithoutStep: true, + expectedWithStep: true, + }, + { + param: 2, + expectedWithoutStep: true, + expectedWithStep: true, + }, + } + + signedContainsTestCasesForward := []containsTestCase{ + { + param: 1, + expectedWithoutStep: true, + expectedWithStep: false, + }, + { + param: 100, + expectedWithoutStep: false, + expectedWithStep: false, + }, + { + param: -100, + expectedWithoutStep: false, + expectedWithStep: false, + }, + { + param: 0, + expectedWithoutStep: true, + expectedWithStep: true, + }, + { + param: 4, + expectedWithoutStep: true, + expectedWithStep: true, + }, + { + param: 10, + expectedWithoutStep: true, + expectedWithStep: true, + }, + } + signedContainsTestCasesBackward := []containsTestCase{ + { + param: 1, + expectedWithoutStep: true, + expectedWithStep: false, + }, + { + param: 12, + expectedWithoutStep: false, + expectedWithStep: false, + }, + { + param: -12, + expectedWithoutStep: false, + expectedWithStep: false, + }, + { + param: 10, + expectedWithoutStep: true, + expectedWithStep: true, + }, + { + param: -10, + expectedWithoutStep: true, + expectedWithStep: true, + }, + { + param: -8, + expectedWithoutStep: true, + expectedWithStep: true, + }, + } + + validTestCases := []inclusiveRangeConstructionTest{ + // Int* + { + ty: sema.IntType, + s: 0, + e: 10, + step: 2, + containsTests: signedContainsTestCasesForward, + }, + { + ty: sema.IntType, + s: 10, + e: -10, + step: -2, + containsTests: signedContainsTestCasesBackward, + }, + { + ty: sema.Int8Type, + s: 0, + e: 10, + step: 2, + containsTests: signedContainsTestCasesForward, + }, + { + ty: sema.Int8Type, + s: 10, + e: -10, + step: -2, + containsTests: signedContainsTestCasesBackward, + }, + { + ty: sema.Int16Type, + s: 0, + e: 10, + step: 2, + containsTests: signedContainsTestCasesForward, + }, + { + ty: sema.Int16Type, + s: 10, + e: -10, + step: -2, + containsTests: signedContainsTestCasesBackward, + }, + { + ty: sema.Int32Type, + s: 0, + e: 10, + step: 2, + containsTests: signedContainsTestCasesForward, + }, + { + ty: sema.Int32Type, + s: 10, + e: -10, + step: -2, + containsTests: signedContainsTestCasesBackward, + }, + { + ty: sema.Int64Type, + s: 0, + e: 10, + step: 2, + containsTests: signedContainsTestCasesForward, + }, + { + ty: sema.Int64Type, + s: 10, + e: -10, + step: -2, + containsTests: signedContainsTestCasesBackward, + }, + { + ty: sema.Int128Type, + s: 0, + e: 10, + step: 2, + containsTests: signedContainsTestCasesForward, + }, + { + ty: sema.Int128Type, + s: 10, + e: -10, + step: -2, + containsTests: signedContainsTestCasesBackward, + }, + { + ty: sema.Int256Type, + s: 0, + e: 10, + step: 2, + containsTests: signedContainsTestCasesForward, + }, + { + ty: sema.Int256Type, + s: 10, + e: -10, + step: -2, + containsTests: signedContainsTestCasesBackward, + }, + + // UInt* + { + ty: sema.UIntType, + s: 0, + e: 10, + step: 2, + containsTests: unsignedContainsTestCases, + }, + { + ty: sema.UInt8Type, + s: 0, + e: 10, + step: 2, + containsTests: unsignedContainsTestCases, + }, + { + ty: sema.UInt16Type, + s: 0, + e: 10, + step: 2, + containsTests: unsignedContainsTestCases, + }, + { + ty: sema.UInt32Type, + s: 0, + e: 10, + step: 2, + containsTests: unsignedContainsTestCases, + }, + { + ty: sema.UInt64Type, + s: 0, + e: 10, + step: 2, + containsTests: unsignedContainsTestCases, + }, + { + ty: sema.UInt128Type, + s: 0, + e: 10, + step: 2, + containsTests: unsignedContainsTestCases, + }, + { + ty: sema.UInt256Type, + s: 0, + e: 10, + step: 2, + containsTests: unsignedContainsTestCases, + }, + + // Word* + { + ty: sema.Word8Type, + s: 0, + e: 10, + step: 2, + containsTests: unsignedContainsTestCases, + }, + { + ty: sema.Word16Type, + s: 0, + e: 10, + step: 2, + containsTests: unsignedContainsTestCases, + }, + { + ty: sema.Word32Type, + s: 0, + e: 10, + step: 2, + containsTests: unsignedContainsTestCases, + }, + { + ty: sema.Word64Type, + s: 0, + e: 10, + step: 2, + containsTests: unsignedContainsTestCases, + }, + { + ty: sema.Word128Type, + s: 0, + e: 10, + step: 2, + containsTests: unsignedContainsTestCases, + }, + { + ty: sema.Word256Type, + s: 0, + e: 10, + step: 2, + containsTests: unsignedContainsTestCases, + }, + } + + runValidCase := func(t *testing.T, testCase inclusiveRangeConstructionTest, withStep bool) { + t.Run(testCase.ty.String(), func(t *testing.T) { t.Parallel() + // Generate code for the contains calls. + var containsCode string + for i, tc := range testCase.containsTests { + containsCode += fmt.Sprintf("\nlet c_%d = r.contains(%d)", i, tc.param) + } + var code string if withStep { code = fmt.Sprintf( ` - let s : %s = 10 - let e : %s = 20 - let step : %s = 2 + let s : %s = %d + let e : %s = %d + let step : %s = %d let r = InclusiveRange(s, e, step: step) - let count = r.count - - let containsTest = r.contains(s) + %s `, - memberType.String(), memberType.String(), memberType.String()) + testCase.ty.String(), + testCase.s, + testCase.ty.String(), + testCase.e, + testCase.ty.String(), + testCase.step, + containsCode, + ) } else { code = fmt.Sprintf( ` - let s : %s = 10 - let e : %s = 20 + let s : %s = %d + let e : %s = %d let r = InclusiveRange(s, e) - let count = r.count - - let containsTest = r.contains(s) + %s `, - memberType.String(), memberType.String()) + testCase.ty.String(), + testCase.s, + testCase.ty.String(), + testCase.e, + containsCode, + ) } - _, err := parseCheckAndInterpretWithOptions(t, code, + inter, err := parseCheckAndInterpretWithOptions(t, code, ParseCheckAndInterpretOptions{ CheckerConfig: &sema.Config{ BaseValueActivation: baseValueActivation, @@ -83,23 +402,164 @@ func TestInclusiveRange(t *testing.T) { ) require.NoError(t, err) + + elementType := interpreter.ConvertSemaToStaticType( + nil, + testCase.ty, + ) + rangeType := interpreter.NewInclusiveRangeStaticType(nil, elementType) + + var expectedRangeValue *interpreter.CompositeValue + + if withStep { + expectedRangeValue = interpreter.NewInclusiveRangeValueWithStep( + inter, + interpreter.EmptyLocationRange, + interpreter.GetValueForIntegerType(testCase.s, elementType), + interpreter.GetValueForIntegerType(testCase.e, elementType), + interpreter.GetValueForIntegerType(testCase.step, elementType), + rangeType, + ) + } else { + expectedRangeValue = interpreter.NewInclusiveRangeValue( + inter, + interpreter.EmptyLocationRange, + interpreter.GetValueForIntegerType(testCase.s, elementType), + interpreter.GetValueForIntegerType(testCase.e, elementType), + rangeType, + ) + } + + utils.AssertValuesEqual( + t, + inter, + expectedRangeValue, + inter.Globals.Get("r").GetValue(), + ) + + // Check that contains returns correct information. + for i, tc := range testCase.containsTests { + var expectedValue interpreter.Value + if withStep { + expectedValue = interpreter.AsBoolValue(tc.expectedWithStep) + } else { + expectedValue = interpreter.AsBoolValue(tc.expectedWithoutStep) + } + + utils.AssertValuesEqual( + t, + inter, + expectedValue, + inter.Globals.Get(fmt.Sprintf("c_%d", i)).GetValue(), + ) + } }) } - runValidCaseWithoutStep := func(t *testing.T, memberType sema.Type) { - runValidCase(t, memberType, false) + // Run each test case with and without step. + for _, testCase := range validTestCases { + runValidCase(t, testCase, true) + runValidCase(t, testCase, false) } - runValidCaseWithStep := func(t *testing.T, memberType sema.Type) { - runValidCase(t, memberType, true) +} + +func TestInclusiveRangeConstructionInvalid(t *testing.T) { + t.Parallel() + + baseValueActivation := sema.NewVariableActivation(sema.BaseValueActivation) + baseValueActivation.DeclareValue(stdlib.InclusiveRangeConstructorFunction) + + baseActivation := activations.NewActivation(nil, interpreter.BaseActivation) + interpreter.Declare(baseActivation, stdlib.InclusiveRangeConstructorFunction) + + runInvalidCase := func(t *testing.T, label, code string, expectedError error, expectedMessage string) { + t.Run(label, func(t *testing.T) { + t.Parallel() + + _, err := parseCheckAndInterpretWithOptions(t, code, + ParseCheckAndInterpretOptions{ + CheckerConfig: &sema.Config{ + BaseValueActivation: baseValueActivation, + }, + Config: &interpreter.Config{ + BaseActivation: baseActivation, + }, + }, + ) + + RequireError(t, err) + + require.ErrorAs(t, err, expectedError) + require.True(t, strings.Contains(err.Error(), expectedMessage)) + }) } for _, integerType := range sema.AllIntegerTypes { + // Only test leaf types switch integerType { case sema.IntegerType, sema.SignedIntegerType: continue } - runValidCaseWithStep(t, integerType) - runValidCaseWithoutStep(t, integerType) + typeString := integerType.String() + + // step = 0. + runInvalidCase( + t, + typeString, + fmt.Sprintf("let r = InclusiveRange(%s(1), %s(2), step: %s(0))", typeString, typeString, typeString), + &interpreter.InclusiveRangeConstructionError{}, + "step value cannot be zero", + ) + + // step takes sequence away from end. + runInvalidCase( + t, + typeString, + fmt.Sprintf("let r = InclusiveRange(%s(40), %s(2), step: %s(2))", typeString, typeString, typeString), + &interpreter.InclusiveRangeConstructionError{}, + "sequence is moving away from end", + ) + } + + // Additional invalid cases for signed integer types + for _, integerType := range sema.AllSignedIntegerTypes { + // Only test leaf types + switch integerType { + case sema.SignedIntegerType: + continue + } + + typeString := integerType.String() + + // step takes sequence away from end with step being negative. + // This would be a checker error for unsigned integers but a + // runtime error in signed integers. + runInvalidCase( + t, + typeString, + fmt.Sprintf("let r = InclusiveRange(%s(4), %s(100), step: %s(-2))", typeString, typeString, typeString), + &interpreter.InclusiveRangeConstructionError{}, + "sequence is moving away from end", + ) + } + + // Additional invalid cases for unsigned integer types + for _, integerType := range sema.AllUnsignedIntegerTypes { + // Only test leaf types + switch integerType { + case sema.IntegerType: + continue + } + + typeString := integerType.String() + + runInvalidCase( + t, + typeString, + fmt.Sprintf("let r = InclusiveRange(%s(40), %s(1))", typeString, typeString), + &interpreter.InclusiveRangeConstructionError{}, + "step value cannot be negative for unsigned integer type", + ) } } From 9458718ff45770db1c2f578a59a7f53d67d70a62 Mon Sep 17 00:00:00 2001 From: darkdrag00nv2 <122124396+darkdrag00nv2@users.noreply.github.com> Date: Tue, 4 Jul 2023 00:33:56 +0530 Subject: [PATCH 007/121] Use nil for location range of composite value MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bastian Müller --- runtime/interpreter/value_range.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/interpreter/value_range.go b/runtime/interpreter/value_range.go index 0683ed6f32..a69a8159f4 100644 --- a/runtime/interpreter/value_range.go +++ b/runtime/interpreter/value_range.go @@ -112,7 +112,7 @@ func NewInclusiveRangeValueWithStep( rangeValue := NewCompositeValueWithStaticType( interpreter, locationRange, - sema.PublicKeyType.Location, // TODO: Figure out a location. + nil rangeSemaType.QualifiedString(), common.CompositeKindStructure, fields, From c962d1c58e6c109f842e956ca3570b2aa241c976 Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Tue, 4 Jul 2023 01:02:46 +0530 Subject: [PATCH 008/121] Add missing semicolon --- runtime/interpreter/value_range.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/interpreter/value_range.go b/runtime/interpreter/value_range.go index a69a8159f4..d655a0d5ab 100644 --- a/runtime/interpreter/value_range.go +++ b/runtime/interpreter/value_range.go @@ -112,7 +112,7 @@ func NewInclusiveRangeValueWithStep( rangeValue := NewCompositeValueWithStaticType( interpreter, locationRange, - nil + nil, rangeSemaType.QualifiedString(), common.CompositeKindStructure, fields, From 5a01ddec37b023813a1a12a42da185ebd129bcb8 Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Tue, 4 Jul 2023 01:21:28 +0530 Subject: [PATCH 009/121] Convert and assert needle value at callsite --- runtime/interpreter/value_range.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/runtime/interpreter/value_range.go b/runtime/interpreter/value_range.go index d655a0d5ab..5c15b3dfb7 100644 --- a/runtime/interpreter/value_range.go +++ b/runtime/interpreter/value_range.go @@ -127,12 +127,14 @@ func NewInclusiveRangeValueWithStep( rangeSemaType.MemberType, ), func(invocation Invocation) Value { + needleInteger := convertAndAssertIntegerValue(invocation.Arguments[0]) + return rangeContains( rangeValue, rangeType, invocation.Interpreter, invocation.LocationRange, - invocation.Arguments[0], + needleInteger, ) }, ), @@ -150,21 +152,19 @@ func rangeContains( rangeType InclusiveRangeStaticType, interpreter *Interpreter, locationRange LocationRange, - needleValue Value, + needleValue IntegerValue, ) BoolValue { start := getFieldAsIntegerValue(rangeValue, interpreter, locationRange, sema.InclusiveRangeTypeStartFieldName) endInclusive := getFieldAsIntegerValue(rangeValue, interpreter, locationRange, sema.InclusiveRangeTypeEndFieldName) step := getFieldAsIntegerValue(rangeValue, interpreter, locationRange, sema.InclusiveRangeTypeStepFieldName) - needleInteger := convertAndAssertIntegerValue(needleValue) - var result bool - result = start.Equal(interpreter, locationRange, needleInteger) || - endInclusive.Equal(interpreter, locationRange, needleInteger) + result = start.Equal(interpreter, locationRange, needleValue) || + endInclusive.Equal(interpreter, locationRange, needleValue) if !result { - greaterThanStart := needleInteger.Greater(interpreter, start, locationRange) - greaterThanEndInclusive := needleInteger.Greater(interpreter, endInclusive, locationRange) + greaterThanStart := needleValue.Greater(interpreter, start, locationRange) + greaterThanEndInclusive := needleValue.Greater(interpreter, endInclusive, locationRange) if greaterThanStart == greaterThanEndInclusive { // If needle is greater or smaller than both start & endInclusive, then it is outside the range. @@ -172,7 +172,7 @@ func rangeContains( } else { // needle is in between start and endInclusive. // start + k * step should be equal to needle i.e. (needle - start) mod step == 0. - diff, ok := needleInteger.Minus(interpreter, start, locationRange).(IntegerValue) + diff, ok := needleValue.Minus(interpreter, start, locationRange).(IntegerValue) if !ok { panic(errors.NewUnreachableError()) } From 87cdcc51f68f29bbb1a27ddd92c4d14074730341 Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Tue, 4 Jul 2023 02:07:35 +0530 Subject: [PATCH 010/121] Store a lazy lookup table for IntegerValue in interpreter --- runtime/interpreter/interpreter.go | 93 ++++++++++++++++--- runtime/interpreter/value_range.go | 62 +------------ runtime/tests/interpreter/range_value_test.go | 10 +- 3 files changed, 92 insertions(+), 73 deletions(-) diff --git a/runtime/interpreter/interpreter.go b/runtime/interpreter/interpreter.go index 22d73df311..a11bf43ec5 100644 --- a/runtime/interpreter/interpreter.go +++ b/runtime/interpreter/interpreter.go @@ -242,14 +242,15 @@ type Storage interface { type ReferencedResourceKindedValues map[atree.StorageID]map[ReferenceTrackedResourceKindedValue]struct{} type Interpreter struct { - Location common.Location - statement ast.Statement - Program *Program - SharedState *SharedState - Globals GlobalVariables - activations *VariableActivations - Transactions []*HostFunctionValue - interpreted bool + Location common.Location + statement ast.Statement + Program *Program + SharedState *SharedState + Globals GlobalVariables + activations *VariableActivations + Transactions []*HostFunctionValue + interpreted bool + cachedIntegerValues map[StaticType]map[int64]IntegerValue } var _ common.MemoryGauge = &Interpreter{} @@ -286,9 +287,10 @@ func NewInterpreterWithSharedState( ) (*Interpreter, error) { interpreter := &Interpreter{ - Program: program, - Location: location, - SharedState: sharedState, + Program: program, + Location: location, + SharedState: sharedState, + cachedIntegerValues: make(map[StaticType]map[int64]IntegerValue), } // Register self @@ -2353,6 +2355,75 @@ func (interpreter *Interpreter) WriteStored( return accountStorage.WriteValue(interpreter, key, value) } +// Get the provided int64 value in the required staticType. +// Note: Assumes that the provided value fits within the constraints of the staticType. +func (interpreter *Interpreter) GetValueForIntegerType(value int64, staticType StaticType) IntegerValue { + typedCache, typedOk := interpreter.cachedIntegerValues[staticType] + if typedOk { + val, ok := typedCache[value] + if ok { + return val + } + } + + val := getValueForIntegerType(value, staticType) + if !typedOk { + interpreter.cachedIntegerValues[staticType] = make(map[int64]IntegerValue) + } + interpreter.cachedIntegerValues[staticType][value] = val + return val +} + +func getValueForIntegerType(value int64, staticType StaticType) IntegerValue { + switch staticType { + case PrimitiveStaticTypeInt: + return NewUnmeteredIntValueFromInt64(value) + case PrimitiveStaticTypeInt8: + return NewUnmeteredInt8Value(int8(value)) + case PrimitiveStaticTypeInt16: + return NewUnmeteredInt16Value(int16(value)) + case PrimitiveStaticTypeInt32: + return NewUnmeteredInt32Value(int32(value)) + case PrimitiveStaticTypeInt64: + return NewUnmeteredInt64Value(value) + case PrimitiveStaticTypeInt128: + return NewUnmeteredInt128ValueFromInt64(value) + case PrimitiveStaticTypeInt256: + return NewUnmeteredInt256ValueFromInt64(value) + + case PrimitiveStaticTypeUInt: + return NewUnmeteredUIntValueFromUint64(uint64(value)) + case PrimitiveStaticTypeUInt8: + return NewUnmeteredUInt8Value(uint8(value)) + case PrimitiveStaticTypeUInt16: + return NewUnmeteredUInt16Value(uint16(value)) + case PrimitiveStaticTypeUInt32: + return NewUnmeteredUInt32Value(uint32(value)) + case PrimitiveStaticTypeUInt64: + return NewUnmeteredUInt64Value(uint64(value)) + case PrimitiveStaticTypeUInt128: + return NewUnmeteredUInt128ValueFromUint64(uint64(value)) + case PrimitiveStaticTypeUInt256: + return NewUnmeteredUInt256ValueFromUint64(uint64(value)) + + case PrimitiveStaticTypeWord8: + return NewUnmeteredWord8Value(uint8(value)) + case PrimitiveStaticTypeWord16: + return NewUnmeteredWord16Value(uint16(value)) + case PrimitiveStaticTypeWord32: + return NewUnmeteredWord32Value(uint32(value)) + case PrimitiveStaticTypeWord64: + return NewUnmeteredWord64Value(uint64(value)) + case PrimitiveStaticTypeWord128: + return NewUnmeteredWord128ValueFromUint64(uint64(value)) + case PrimitiveStaticTypeWord256: + return NewUnmeteredWord256ValueFromUint64(uint64(value)) + + default: + panic(errors.NewUnreachableError()) + } +} + type fromStringFunctionValue struct { receiverType sema.Type hostFunction *HostFunctionValue diff --git a/runtime/interpreter/value_range.go b/runtime/interpreter/value_range.go index 5c15b3dfb7..1c88040007 100644 --- a/runtime/interpreter/value_range.go +++ b/runtime/interpreter/value_range.go @@ -40,7 +40,7 @@ func NewInclusiveRangeValue( panic(errors.NewUnreachableError()) } - step := GetValueForIntegerType(1, rangeType.ElementType) + step := interpreter.GetValueForIntegerType(1, rangeType.ElementType) if startComparable.Greater(interpreter, endInclusiveComparable, locationRange) { elemSemaTy := interpreter.MustConvertStaticToSemaType(rangeType.ElementType) if _, ok := sema.AllUnsignedIntegerTypesSet[elemSemaTy]; ok { @@ -72,7 +72,7 @@ func NewInclusiveRangeValueWithStep( ) *CompositeValue { // Validate that the step is non-zero. - if step.Equal(interpreter, locationRange, GetValueForIntegerType(0, rangeType.ElementType)) { + if step.Equal(interpreter, locationRange, interpreter.GetValueForIntegerType(0, rangeType.ElementType)) { panic(InclusiveRangeConstructionError{ LocationRange: locationRange, Message: "step value cannot be zero", @@ -83,8 +83,8 @@ func NewInclusiveRangeValueWithStep( // If start < end, step must be > 0 // If start > end, step must be < 0 // If start == end, step doesn't matter. - if (start.Less(interpreter, end, locationRange) && step.Less(interpreter, GetValueForIntegerType(0, rangeType.ElementType), locationRange)) || - (start.Greater(interpreter, end, locationRange) && step.Greater(interpreter, GetValueForIntegerType(0, rangeType.ElementType), locationRange)) { + if (start.Less(interpreter, end, locationRange) && step.Less(interpreter, interpreter.GetValueForIntegerType(0, rangeType.ElementType), locationRange)) || + (start.Greater(interpreter, end, locationRange) && step.Greater(interpreter, interpreter.GetValueForIntegerType(0, rangeType.ElementType), locationRange)) { panic(InclusiveRangeConstructionError{ LocationRange: locationRange, @@ -177,65 +177,13 @@ func rangeContains( panic(errors.NewUnreachableError()) } - result = diff.Mod(interpreter, step, locationRange).Equal(interpreter, locationRange, GetValueForIntegerType(0, rangeType.ElementType)) + result = diff.Mod(interpreter, step, locationRange).Equal(interpreter, locationRange, interpreter.GetValueForIntegerType(0, rangeType.ElementType)) } } return AsBoolValue(result) } -// Get the provided int64 value in the required staticType. -// Note: Assumes that the provided value fits within the constraints of the staticType. -func GetValueForIntegerType(value int64, staticType StaticType) IntegerValue { - switch staticType { - case PrimitiveStaticTypeInt: - return NewUnmeteredIntValueFromInt64(value) - case PrimitiveStaticTypeInt8: - return NewUnmeteredInt8Value(int8(value)) - case PrimitiveStaticTypeInt16: - return NewUnmeteredInt16Value(int16(value)) - case PrimitiveStaticTypeInt32: - return NewUnmeteredInt32Value(int32(value)) - case PrimitiveStaticTypeInt64: - return NewUnmeteredInt64Value(value) - case PrimitiveStaticTypeInt128: - return NewUnmeteredInt128ValueFromInt64(value) - case PrimitiveStaticTypeInt256: - return NewUnmeteredInt256ValueFromInt64(value) - - case PrimitiveStaticTypeUInt: - return NewUnmeteredUIntValueFromUint64(uint64(value)) - case PrimitiveStaticTypeUInt8: - return NewUnmeteredUInt8Value(uint8(value)) - case PrimitiveStaticTypeUInt16: - return NewUnmeteredUInt16Value(uint16(value)) - case PrimitiveStaticTypeUInt32: - return NewUnmeteredUInt32Value(uint32(value)) - case PrimitiveStaticTypeUInt64: - return NewUnmeteredUInt64Value(uint64(value)) - case PrimitiveStaticTypeUInt128: - return NewUnmeteredUInt128ValueFromUint64(uint64(value)) - case PrimitiveStaticTypeUInt256: - return NewUnmeteredUInt256ValueFromUint64(uint64(value)) - - case PrimitiveStaticTypeWord8: - return NewUnmeteredWord8Value(uint8(value)) - case PrimitiveStaticTypeWord16: - return NewUnmeteredWord16Value(uint16(value)) - case PrimitiveStaticTypeWord32: - return NewUnmeteredWord32Value(uint32(value)) - case PrimitiveStaticTypeWord64: - return NewUnmeteredWord64Value(uint64(value)) - case PrimitiveStaticTypeWord128: - return NewUnmeteredWord128ValueFromUint64(uint64(value)) - case PrimitiveStaticTypeWord256: - return NewUnmeteredWord256ValueFromUint64(uint64(value)) - - default: - panic(errors.NewUnreachableError()) - } -} - func getFieldAsIntegerValue( rangeValue *CompositeValue, interpreter *Interpreter, diff --git a/runtime/tests/interpreter/range_value_test.go b/runtime/tests/interpreter/range_value_test.go index cb17855eec..c84a88760f 100644 --- a/runtime/tests/interpreter/range_value_test.go +++ b/runtime/tests/interpreter/range_value_test.go @@ -415,17 +415,17 @@ func TestInclusiveRange(t *testing.T) { expectedRangeValue = interpreter.NewInclusiveRangeValueWithStep( inter, interpreter.EmptyLocationRange, - interpreter.GetValueForIntegerType(testCase.s, elementType), - interpreter.GetValueForIntegerType(testCase.e, elementType), - interpreter.GetValueForIntegerType(testCase.step, elementType), + inter.GetValueForIntegerType(testCase.s, elementType), + inter.GetValueForIntegerType(testCase.e, elementType), + inter.GetValueForIntegerType(testCase.step, elementType), rangeType, ) } else { expectedRangeValue = interpreter.NewInclusiveRangeValue( inter, interpreter.EmptyLocationRange, - interpreter.GetValueForIntegerType(testCase.s, elementType), - interpreter.GetValueForIntegerType(testCase.e, elementType), + inter.GetValueForIntegerType(testCase.s, elementType), + inter.GetValueForIntegerType(testCase.e, elementType), rangeType, ) } From ac00e1e0065b79b1cbeeacd2705e5e0c5014cf04 Mon Sep 17 00:00:00 2001 From: darkdrag00nv2 <122124396+darkdrag00nv2@users.noreply.github.com> Date: Sun, 9 Jul 2023 17:39:08 +0530 Subject: [PATCH 011/121] Format the error message for sequence moving away from end. Co-authored-by: Supun Setunga --- runtime/interpreter/value_range.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/runtime/interpreter/value_range.go b/runtime/interpreter/value_range.go index 1c88040007..2b47bb1621 100644 --- a/runtime/interpreter/value_range.go +++ b/runtime/interpreter/value_range.go @@ -88,7 +88,12 @@ func NewInclusiveRangeValueWithStep( panic(InclusiveRangeConstructionError{ LocationRange: locationRange, - Message: fmt.Sprintf("sequence is moving away from end: %s due to the value of step: %s and start: %s", end, step, start), + Message: fmt.Sprintf( + "sequence is moving away from end: %s due to the value of step: %s and start: %s", + end, + step, + start, + ), }) } From fc47776c6f3626d75edff40e3473ac158a04b666 Mon Sep 17 00:00:00 2001 From: darkdrag00nv2 <122124396+darkdrag00nv2@users.noreply.github.com> Date: Sun, 9 Jul 2023 17:40:05 +0530 Subject: [PATCH 012/121] Break mod operation into multiple lines. Co-authored-by: Supun Setunga --- runtime/interpreter/value_range.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/runtime/interpreter/value_range.go b/runtime/interpreter/value_range.go index 2b47bb1621..a2dcf23377 100644 --- a/runtime/interpreter/value_range.go +++ b/runtime/interpreter/value_range.go @@ -182,7 +182,8 @@ func rangeContains( panic(errors.NewUnreachableError()) } - result = diff.Mod(interpreter, step, locationRange).Equal(interpreter, locationRange, interpreter.GetValueForIntegerType(0, rangeType.ElementType)) + zeroValue := interpreter.GetValueForIntegerType(0, rangeType.ElementType) + result = diff.Mod(interpreter, step, locationRange).Equal(interpreter, locationRange, zeroValue) } } From 74af213c1894e66508bfe156bbe5179ecb9bdf69 Mon Sep 17 00:00:00 2001 From: darkdrag00nv2 <122124396+darkdrag00nv2@users.noreply.github.com> Date: Sun, 9 Jul 2023 17:41:58 +0530 Subject: [PATCH 013/121] Format NewInclusiveRangeValueWithStep call into multiple lines Co-authored-by: Supun Setunga --- runtime/stdlib/range.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/runtime/stdlib/range.go b/runtime/stdlib/range.go index 7d12f946fb..29d4a1b512 100644 --- a/runtime/stdlib/range.go +++ b/runtime/stdlib/range.go @@ -104,7 +104,14 @@ var InclusiveRangeConstructorFunction = NewStandardLibraryFunction( panic(errors.NewUnreachableError()) } - return interpreter.NewInclusiveRangeValueWithStep(inter, locationRange, start, endInclusive, step, rangeStaticType) + return interpreter.NewInclusiveRangeValueWithStep( + inter, + locationRange, + start, + endInclusive, + step, + rangeStaticType, + ) } else { return interpreter.NewInclusiveRangeValue(inter, locationRange, start, endInclusive, rangeStaticType) } From 18b485984b6c7855dfe859af264b132b9cc05092 Mon Sep 17 00:00:00 2001 From: darkdrag00nv2 <122124396+darkdrag00nv2@users.noreply.github.com> Date: Sun, 9 Jul 2023 17:43:34 +0530 Subject: [PATCH 014/121] Declare and initialize result on the same line Co-authored-by: Supun Setunga --- runtime/interpreter/value_range.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/runtime/interpreter/value_range.go b/runtime/interpreter/value_range.go index a2dcf23377..8df608fb63 100644 --- a/runtime/interpreter/value_range.go +++ b/runtime/interpreter/value_range.go @@ -163,8 +163,7 @@ func rangeContains( endInclusive := getFieldAsIntegerValue(rangeValue, interpreter, locationRange, sema.InclusiveRangeTypeEndFieldName) step := getFieldAsIntegerValue(rangeValue, interpreter, locationRange, sema.InclusiveRangeTypeStepFieldName) - var result bool - result = start.Equal(interpreter, locationRange, needleValue) || + result := start.Equal(interpreter, locationRange, needleValue) || endInclusive.Equal(interpreter, locationRange, needleValue) if !result { From a867b6557e03f299407544527d75d037a334c798 Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Sun, 9 Jul 2023 17:56:36 +0530 Subject: [PATCH 015/121] fix lint --- runtime/interpreter/value_range.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/runtime/interpreter/value_range.go b/runtime/interpreter/value_range.go index 8df608fb63..5899bf25c6 100644 --- a/runtime/interpreter/value_range.go +++ b/runtime/interpreter/value_range.go @@ -88,11 +88,11 @@ func NewInclusiveRangeValueWithStep( panic(InclusiveRangeConstructionError{ LocationRange: locationRange, - Message: fmt.Sprintf( + Message: fmt.Sprintf( "sequence is moving away from end: %s due to the value of step: %s and start: %s", - end, - step, - start, + end, + step, + start, ), }) } From e36333e5dd196e44e12c67c6c909c7204f8cfdf2 Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Sun, 9 Jul 2023 17:59:04 +0530 Subject: [PATCH 016/121] return early in contains if needle = lower/upper bound --- runtime/interpreter/value_range.go | 37 ++++++++++++++++-------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/runtime/interpreter/value_range.go b/runtime/interpreter/value_range.go index 5899bf25c6..211a56f0dc 100644 --- a/runtime/interpreter/value_range.go +++ b/runtime/interpreter/value_range.go @@ -166,24 +166,27 @@ func rangeContains( result := start.Equal(interpreter, locationRange, needleValue) || endInclusive.Equal(interpreter, locationRange, needleValue) - if !result { - greaterThanStart := needleValue.Greater(interpreter, start, locationRange) - greaterThanEndInclusive := needleValue.Greater(interpreter, endInclusive, locationRange) - - if greaterThanStart == greaterThanEndInclusive { - // If needle is greater or smaller than both start & endInclusive, then it is outside the range. - result = false - } else { - // needle is in between start and endInclusive. - // start + k * step should be equal to needle i.e. (needle - start) mod step == 0. - diff, ok := needleValue.Minus(interpreter, start, locationRange).(IntegerValue) - if !ok { - panic(errors.NewUnreachableError()) - } - - zeroValue := interpreter.GetValueForIntegerType(0, rangeType.ElementType) - result = diff.Mod(interpreter, step, locationRange).Equal(interpreter, locationRange, zeroValue) + if result { + return TrueValue + } + + greaterThanStart := needleValue.Greater(interpreter, start, locationRange) + greaterThanEndInclusive := needleValue.Greater(interpreter, endInclusive, locationRange) + + if greaterThanStart == greaterThanEndInclusive { + // If needle is greater or smaller than both start & endInclusive, then it is outside the range. + result = false + } else { + // needle is in between start and endInclusive. + // start + k * step should be equal to needle i.e. (needle - start) mod step == 0. + diff, ok := needleValue.Minus(interpreter, start, locationRange).(IntegerValue) + if !ok { + panic(errors.NewUnreachableError()) } + + zeroValue := interpreter.GetValueForIntegerType(0, rangeType.ElementType) + mod := diff.Mod(interpreter, step, locationRange) + result = mod.Equal(interpreter, locationRange, zeroValue) } return AsBoolValue(result) From d36a76eaa4348531f28396d9ff7c44e06c3202eb Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Sun, 9 Jul 2023 18:00:51 +0530 Subject: [PATCH 017/121] remove runtime/format/range.go as it not needed --- runtime/format/range.go | 27 --------------------------- 1 file changed, 27 deletions(-) delete mode 100644 runtime/format/range.go diff --git a/runtime/format/range.go b/runtime/format/range.go deleted file mode 100644 index 93d1b1ee73..0000000000 --- a/runtime/format/range.go +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Cadence - The resource-oriented smart contract programming language - * - * Copyright Dapper Labs, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package format - -import ( - "fmt" -) - -func InclusiveRange(start, end string) string { - return fmt.Sprintf("InclusiveRange{ start: %s, end: %s }", start, end) -} From 91ea40fda14bd85e30f6b6a1b0b6a2548fb0cbcb Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Sat, 22 Jul 2023 00:46:12 +0530 Subject: [PATCH 018/121] Add import/export support for InclusiveRange --- encoding/ccf/consts.go | 4 +- encoding/ccf/decode.go | 65 ++++ encoding/ccf/decode_type.go | 44 +++ encoding/ccf/encode.go | 54 ++++ encoding/ccf/encode_type.go | 49 +++ encoding/ccf/traverse_value.go | 3 +- encoding/json/decode.go | 26 ++ encoding/json/encode.go | 114 ++++--- runtime/common/memorykind.go | 2 + runtime/common/memorykind_string.go | 280 +++++++++--------- runtime/common/metering.go | 16 +- runtime/convertValues.go | 134 ++++++++- runtime/convertValues_test.go | 85 ++++++ runtime/interpreter/interpreter.go | 30 ++ runtime/interpreter/statictype.go | 4 + runtime/interpreter/storage_test.go | 55 ++++ runtime/interpreter/value_range.go | 26 +- runtime/sema/runtime_type_constructors.go | 22 ++ runtime/sema/type.go | 53 +++- runtime/sema/type_tags.go | 21 +- runtime/stdlib/range.go | 38 ++- runtime/tests/checker/range_value_test.go | 8 +- runtime/tests/interpreter/range_value_test.go | 27 +- values.go | 63 ++++ 24 files changed, 991 insertions(+), 232 deletions(-) diff --git a/encoding/ccf/consts.go b/encoding/ccf/consts.go index ee01a62912..d918cfc821 100644 --- a/encoding/ccf/consts.go +++ b/encoding/ccf/consts.go @@ -64,7 +64,7 @@ const ( CBORTagReferenceType CBORTagRestrictedType CBORTagCapabilityType - _ + CBORTagInclusiveRangeType _ _ _ @@ -120,7 +120,7 @@ const ( CBORTagRestrictedTypeValue CBORTagCapabilityTypeValue CBORTagFunctionTypeValue - _ + CBORTagInclusiveRangeTypeValue // InclusiveRange is stored as a composite value. _ _ _ diff --git a/encoding/ccf/decode.go b/encoding/ccf/decode.go index a4b5b5808b..8d6d3fb8cf 100644 --- a/encoding/ccf/decode.go +++ b/encoding/ccf/decode.go @@ -358,6 +358,7 @@ func (d *Decoder) decodeTypeAndValue(types *cadenceTypeByCCFTypeID) (cadence.Val // / path-value // / path-capability-value // / id-capability-value +// / inclusiverange-value // / function-value // / type-value // @@ -507,6 +508,9 @@ func (d *Decoder) decodeValue(t cadence.Type, types *cadenceTypeByCCFTypeID) (ca case *cadence.ResourceType: return d.decodeResource(t, types) + case *cadence.InclusiveRangeType: + return d.decodeInclusiveRange(t, types) + case *cadence.StructType: return d.decodeStruct(t, types) @@ -1309,6 +1313,63 @@ func (d *Decoder) decodeEnum(typ *cadence.EnumType, types *cadenceTypeByCCFTypeI return v.WithType(typ), nil } +// decodeInclusiveRange decodes encoded inclusiverange-value as +// language=CDDL +// inclusiverange-value = [ +// +// start: value, +// end: value, +// step: value, +// +// ] +func (d *Decoder) decodeInclusiveRange(typ *cadence.InclusiveRangeType, types *cadenceTypeByCCFTypeID) (cadence.Value, error) { + // Decode array length. + n, err := d.dec.DecodeArrayHead() + if err != nil { + return nil, err + } + + if n != 3 { + return nil, fmt.Errorf( + "encoded inclusiverange-value has %d elements (expected %d elements)", + n, + 3, + ) + } + + elementType := typ.ElementType + + // Decode start. + start, err := d.decodeValue(elementType, types) + if err != nil { + return nil, err + } + + // Decode end. + end, err := d.decodeValue(elementType, types) + if err != nil { + return nil, err + } + + // Decode step. + step, err := d.decodeValue(elementType, types) + if err != nil { + return nil, err + } + + v, err := cadence.NewMeteredInclusiveRange( + d.gauge, + start, + end, + step, + ) + if err != nil { + return nil, err + } + + return v.WithType(typ), nil +} + // decodePath decodes path-value as // language=CDDL // path-value = [ @@ -1442,6 +1503,7 @@ func (d *Decoder) decodeCapability(typ *cadence.CapabilityType, types *cadenceTy // / varsized-array-type-value // / constsized-array-type-value // / dict-type-value +// / inclusiverange-type-value // / struct-type-value // / resource-type-value // / contract-type-value @@ -1482,6 +1544,9 @@ func (d *Decoder) decodeTypeValue(visited *cadenceTypeByCCFTypeID) (cadence.Type case CBORTagDictTypeValue: return d.decodeDictType(visited, d.decodeTypeValue) + case CBORTagInclusiveRangeTypeValue: + return d.decodeInclusiveRangeType(visited, d.decodeTypeValue) + case CBORTagCapabilityTypeValue: return d.decodeCapabilityType(visited, d.decodeNullableTypeValue) diff --git a/encoding/ccf/decode_type.go b/encoding/ccf/decode_type.go index d461d40a24..3067a143af 100644 --- a/encoding/ccf/decode_type.go +++ b/encoding/ccf/decode_type.go @@ -45,6 +45,7 @@ type decodeTypeFn func(types *cadenceTypeByCCFTypeID) (cadence.Type, error) // / reference-type // / restricted-type // / capability-type +// / inclusiverange-type // / type-ref // // All exported Cadence types needs to be handled in this function, @@ -71,6 +72,9 @@ func (d *Decoder) decodeInlineType(types *cadenceTypeByCCFTypeID) (cadence.Type, case CBORTagDictType: return d.decodeDictType(types, d.decodeInlineType) + case CBORTagInclusiveRangeType: + return d.decodeInclusiveRangeType(types, d.decodeInlineType) + case CBORTagReferenceType: return d.decodeReferenceType(types, d.decodeInlineType) @@ -436,6 +440,46 @@ func (d *Decoder) decodeDictType( return cadence.NewMeteredDictionaryType(d.gauge, keyType, elementType), nil } +// decodeInclusiveRangeType decodes inclusiverange-type or inclusiverange-type-value as +// language=CDDL +// inclusiverange-type = +// +// ; cbor-tag-inclusiverange-type +// #6.145([ +// element-type: inline-type +// ]) +// +// inclusiverange-type-value = +// +// ; cbor-tag-inclusiverange-type-value +// #6.194([ +// element-type: type-value +// ]) +// +// NOTE: decodeTypeFn is responsible for decoding inline-type or type-value. +func (d *Decoder) decodeInclusiveRangeType( + types *cadenceTypeByCCFTypeID, + decodeTypeFn decodeTypeFn, +) (cadence.Type, error) { + // Decode array head of length 1. + err := decodeCBORArrayWithKnownSize(d.dec, 1) + if err != nil { + return nil, err + } + + // element 0: element type (inline-type or type-value) + elementType, err := decodeTypeFn(types) + if err != nil { + return nil, err + } + + if elementType == nil { + return nil, errors.New("unexpected nil type as inclusiverange element type") + } + + return cadence.NewMeteredInclusiveRangeType(d.gauge, elementType), nil +} + // decodeCapabilityType decodes capability-type or capability-type-value as // language=CDDL // capability-type = diff --git a/encoding/ccf/encode.go b/encoding/ccf/encode.go index ab237ec2e8..3cf76ba329 100644 --- a/encoding/ccf/encode.go +++ b/encoding/ccf/encode.go @@ -378,6 +378,7 @@ func (e *Encoder) encodeTypeDefs(types []cadence.Type, tids ccfTypeIDByCadenceTy // / path-value // / path-capability-value // / id-capability-value +// / inclusiverange-value // / function-value // / type-value // @@ -563,6 +564,9 @@ func (e *Encoder) encodeValue( case cadence.Dictionary: return e.encodeDictionary(v, tids) + case cadence.InclusiveRange: + return e.encodeInclusiveRange(v, tids) + case cadence.Struct: return e.encodeStruct(v, tids) @@ -977,6 +981,34 @@ func encodeAndSortKeyValuePairs( return encodedPairs, nil } +// encodeInclusiveRange encodes cadence.InclusiveRange as +// language=CDDL +// inclusiverange-value = [* (key: value, value: value)] +func (e *Encoder) encodeInclusiveRange(v cadence.InclusiveRange, tids ccfTypeIDByCadenceType) error { + staticElementType := v.InclusiveRangeType.ElementType + + // Encode array head with array size of 3. + err := e.enc.EncodeArrayHead(3) + if err != nil { + return err + } + + // Encode start key as value. + err = e.encodeValue(v.Start, staticElementType, tids) + if err != nil { + return err + } + + // Encode end as value. + err = e.encodeValue(v.End, staticElementType, tids) + if err != nil { + return err + } + + // Encode step key as value. + return e.encodeValue(v.Step, staticElementType, tids) +} + // encodeStruct encodes cadence.Struct as // language=CDDL // composite-value = [* (field: value)] @@ -1232,6 +1264,7 @@ func (e *Encoder) encodeFunction(typ *cadence.FunctionType, visited ccfTypeIDByC // / reference-type-value // / restricted-type-value // / capability-type-value +// / inclusiverange-type-value // / type-value-ref // // TypeValue is used differently from inline type or type definition. @@ -1282,6 +1315,9 @@ func (e *Encoder) encodeTypeValue(typ cadence.Type, visited ccfTypeIDByCadenceTy case *cadence.ContractType: return e.encodeContractTypeValue(typ, visited) + case *cadence.InclusiveRangeType: + return e.encodeInclusiveRangeTypeValue(typ, visited) + case *cadence.StructInterfaceType: return e.encodeStructInterfaceTypeValue(typ, visited) @@ -1411,6 +1447,24 @@ func (e *Encoder) encodeDictTypeValue(typ *cadence.DictionaryType, visited ccfTy ) } +// encodeInclusiveRangeTypeValue encodes cadence.InclusiveRangeType as +// language=CDDL +// inclusiverange-type-value = +// +// ; cbor-tag-inclusiverange-type-value +// #6.194([ +// element-type: type-value +// ]) +func (e *Encoder) encodeInclusiveRangeTypeValue(typ *cadence.InclusiveRangeType, visited ccfTypeIDByCadenceType) error { + rawTagNum := []byte{0xd8, CBORTagInclusiveRangeTypeValue} + return e.encodeInclusiveRangeTypeWithRawTag( + typ, + visited, + e.encodeTypeValue, + rawTagNum, + ) +} + // encodeReferenceTypeValue encodes cadence.ReferenceType as // language=CDDL // reference-type-value = diff --git a/encoding/ccf/encode_type.go b/encoding/ccf/encode_type.go index e725bfa0d0..9658aebd78 100644 --- a/encoding/ccf/encode_type.go +++ b/encoding/ccf/encode_type.go @@ -39,6 +39,7 @@ type encodeTypeFn func(typ cadence.Type, tids ccfTypeIDByCadenceType) error // / reference-type // / restricted-type // / capability-type +// / inclusiverange-type // / type-ref // // All exported Cadence types need to be supported by this function, @@ -62,6 +63,9 @@ func (e *Encoder) encodeInlineType(typ cadence.Type, tids ccfTypeIDByCadenceType case *cadence.DictionaryType: return e.encodeDictType(typ, tids) + case *cadence.InclusiveRangeType: + return e.encodeInclusiveRangeType(typ, tids) + case cadence.CompositeType, cadence.InterfaceType: id, err := tids.id(typ) if err != nil { @@ -296,6 +300,51 @@ func (e *Encoder) encodeDictTypeWithRawTag( return encodeTypeFn(typ.ElementType, tids) } +// encodeInclusiveRangeType encodes cadence.InclusiveRangeType as +// language=CDDL +// inclusiverange-type = +// +// ; cbor-tag-inclusiverange-type +// #6.145([ +// element-type: inline-type +// ]) +func (e *Encoder) encodeInclusiveRangeType( + typ *cadence.InclusiveRangeType, + tids ccfTypeIDByCadenceType, +) error { + rawTagNum := []byte{0xd8, CBORTagInclusiveRangeType} + return e.encodeInclusiveRangeTypeWithRawTag( + typ, + tids, + e.encodeInlineType, + rawTagNum, + ) +} + +// encodeDictTypeWithRawTag encodes cadence.InclusiveRangeType +// with given tag number and encode type function. +func (e *Encoder) encodeInclusiveRangeTypeWithRawTag( + typ *cadence.InclusiveRangeType, + tids ccfTypeIDByCadenceType, + encodeTypeFn encodeTypeFn, + rawTagNumber []byte, +) error { + // Encode CBOR tag number. + err := e.enc.EncodeRawBytes(rawTagNumber) + if err != nil { + return err + } + + // Encode array head of length 1. + err = e.enc.EncodeArrayHead(1) + if err != nil { + return err + } + + // element 0: element type with given encodeTypeFn + return encodeTypeFn(typ.ElementType, tids) +} + // encodeReferenceType encodes cadence.ReferenceType as // language=CDDL // reference-type = diff --git a/encoding/ccf/traverse_value.go b/encoding/ccf/traverse_value.go index d82c83891c..ee982cf43e 100644 --- a/encoding/ccf/traverse_value.go +++ b/encoding/ccf/traverse_value.go @@ -220,7 +220,8 @@ func (ct *compositeTypes) traverseType(typ cadence.Type) (checkRuntimeType bool) cadence.IntegerType, cadence.SignedIntegerType, cadence.FixedPointType, - cadence.SignedFixedPointType: + cadence.SignedFixedPointType, + *cadence.InclusiveRangeType: // TODO: Maybe there are more types that we can skip checking runtime type for composite type. return false diff --git a/encoding/json/decode.go b/encoding/json/decode.go index 1c7d25997c..30dde19be8 100644 --- a/encoding/json/decode.go +++ b/encoding/json/decode.go @@ -136,6 +136,9 @@ const ( returnKey = "return" typeBoundKey = "typeBound" functionTypeKey = "functionType" + startKey = "start" + endKey = "end" + stepKey = "step" ) func (d *Decoder) decodeJSON(v any) cadence.Value { @@ -222,6 +225,8 @@ func (d *Decoder) decodeJSON(v any) cadence.Value { return d.decodeEvent(valueJSON) case contractTypeStr: return d.decodeContract(valueJSON) + case inclusiveRangeTypeStr: + return d.decodeInclusiveRange(valueJSON) case pathTypeStr: return d.decodePath(valueJSON) case typeTypeStr: @@ -866,6 +871,27 @@ func (d *Decoder) decodeEnum(valueJSON any) cadence.Enum { )) } +func (d *Decoder) decodeInclusiveRange(valueJSON any) cadence.InclusiveRange { + obj := toObject(valueJSON) + + start := obj.GetValue(d, startKey) + end := obj.GetValue(d, endKey) + step := obj.GetValue(d, stepKey) + + value, err := cadence.NewMeteredInclusiveRange( + d.gauge, + start, + end, + step, + ) + + if err != nil { + panic(errors.NewDefaultUserError("invalid InclusiveRange: %w", err)) + } + + return value +} + func (d *Decoder) decodePath(valueJSON any) cadence.Path { obj := toObject(valueJSON) diff --git a/encoding/json/encode.go b/encoding/json/encode.go index b0c47bf4f1..1c9ec52df1 100644 --- a/encoding/json/encode.go +++ b/encoding/json/encode.go @@ -116,6 +116,12 @@ type jsonDictionaryItem struct { Value jsonValue `json:"value"` } +type jsonInclusiveRangeValue struct { + Start jsonValue `json:"start"` + End jsonValue `json:"end"` + Step jsonValue `json:"step"` +} + type jsonCompositeValue struct { ID string `json:"id"` Fields []jsonCompositeField `json:"fields"` @@ -170,6 +176,11 @@ type jsonDictionaryType struct { Kind string `json:"kind"` } +type jsonInclusiveRangeType struct { + ElementType jsonValue `json:"element"` + Kind string `json:"kind"` +} + type jsonReferenceType struct { Type jsonValue `json:"type"` Kind string `json:"kind"` @@ -223,48 +234,49 @@ type jsonFunctionValue struct { } const ( - voidTypeStr = "Void" - optionalTypeStr = "Optional" - boolTypeStr = "Bool" - characterTypeStr = "Character" - stringTypeStr = "String" - addressTypeStr = "Address" - intTypeStr = "Int" - int8TypeStr = "Int8" - int16TypeStr = "Int16" - int32TypeStr = "Int32" - int64TypeStr = "Int64" - int128TypeStr = "Int128" - int256TypeStr = "Int256" - uintTypeStr = "UInt" - uint8TypeStr = "UInt8" - uint16TypeStr = "UInt16" - uint32TypeStr = "UInt32" - uint64TypeStr = "UInt64" - uint128TypeStr = "UInt128" - uint256TypeStr = "UInt256" - word8TypeStr = "Word8" - word16TypeStr = "Word16" - word32TypeStr = "Word32" - word64TypeStr = "Word64" - word128TypeStr = "Word128" - word256TypeStr = "Word256" - fix64TypeStr = "Fix64" - ufix64TypeStr = "UFix64" - arrayTypeStr = "Array" - dictionaryTypeStr = "Dictionary" - structTypeStr = "Struct" - resourceTypeStr = "Resource" - attachmentTypeStr = "Attachment" - eventTypeStr = "Event" - contractTypeStr = "Contract" - linkTypeStr = "Link" - accountLinkTypeStr = "AccountLink" - pathTypeStr = "Path" - typeTypeStr = "Type" - capabilityTypeStr = "Capability" - enumTypeStr = "Enum" - functionTypeStr = "Function" + voidTypeStr = "Void" + optionalTypeStr = "Optional" + boolTypeStr = "Bool" + characterTypeStr = "Character" + stringTypeStr = "String" + addressTypeStr = "Address" + intTypeStr = "Int" + int8TypeStr = "Int8" + int16TypeStr = "Int16" + int32TypeStr = "Int32" + int64TypeStr = "Int64" + int128TypeStr = "Int128" + int256TypeStr = "Int256" + uintTypeStr = "UInt" + uint8TypeStr = "UInt8" + uint16TypeStr = "UInt16" + uint32TypeStr = "UInt32" + uint64TypeStr = "UInt64" + uint128TypeStr = "UInt128" + uint256TypeStr = "UInt256" + word8TypeStr = "Word8" + word16TypeStr = "Word16" + word32TypeStr = "Word32" + word64TypeStr = "Word64" + word128TypeStr = "Word128" + word256TypeStr = "Word256" + fix64TypeStr = "Fix64" + ufix64TypeStr = "UFix64" + arrayTypeStr = "Array" + dictionaryTypeStr = "Dictionary" + structTypeStr = "Struct" + resourceTypeStr = "Resource" + attachmentTypeStr = "Attachment" + eventTypeStr = "Event" + contractTypeStr = "Contract" + inclusiveRangeTypeStr = "InclusiveRange" + linkTypeStr = "Link" + accountLinkTypeStr = "AccountLink" + pathTypeStr = "Path" + typeTypeStr = "Type" + capabilityTypeStr = "Capability" + enumTypeStr = "Enum" + functionTypeStr = "Function" ) // Prepare traverses the object graph of the provided value and constructs @@ -331,6 +343,8 @@ func Prepare(v cadence.Value) jsonValue { return prepareArray(v) case cadence.Dictionary: return prepareDictionary(v) + case cadence.InclusiveRange: + return prepareInclusiveRange(v) case cadence.Struct: return prepareStruct(v) case cadence.Resource: @@ -592,6 +606,17 @@ func prepareDictionary(v cadence.Dictionary) jsonValue { } } +func prepareInclusiveRange(v cadence.InclusiveRange) jsonValue { + return jsonValueObject{ + Type: inclusiveRangeTypeStr, + Value: jsonInclusiveRangeValue{ + Start: Prepare(v.Start), + End: Prepare(v.End), + Step: Prepare(v.Step), + }, + } +} + func prepareStruct(v cadence.Struct) jsonValue { return prepareComposite(structTypeStr, v.StructType.ID(), v.StructType.Fields, v.Fields) } @@ -841,6 +866,11 @@ func prepareType(typ cadence.Type, results typePreparationResults) jsonValue { KeyType: prepareType(typ.KeyType, results), ValueType: prepareType(typ.ElementType, results), } + case *cadence.InclusiveRangeType: + return jsonInclusiveRangeType{ + Kind: "InclusiveRange", + ElementType: prepareType(typ.ElementType, results), + } case *cadence.StructType: return jsonNominalType{ Kind: "Struct", diff --git a/runtime/common/memorykind.go b/runtime/common/memorykind.go index 357ff2a712..c35c04fd96 100644 --- a/runtime/common/memorykind.go +++ b/runtime/common/memorykind.go @@ -91,6 +91,7 @@ const ( MemoryKindCadenceArrayValueBase MemoryKindCadenceArrayValueLength MemoryKindCadenceDictionaryValue + MemoryKindCadenceInclusiveRangeValue MemoryKindCadenceKeyValuePair MemoryKindCadenceStructValueBase MemoryKindCadenceStructValueSize @@ -243,6 +244,7 @@ const ( MemoryKindRestrictedSemaType MemoryKindReferenceSemaType MemoryKindCapabilitySemaType + MemoryKindInclusiveRangeSemaType // ordered-map MemoryKindOrderedMap diff --git a/runtime/common/memorykind_string.go b/runtime/common/memorykind_string.go index 5a5580fa4a..a84eae199b 100644 --- a/runtime/common/memorykind_string.go +++ b/runtime/common/memorykind_string.go @@ -66,148 +66,150 @@ func _() { _ = x[MemoryKindCadenceArrayValueBase-55] _ = x[MemoryKindCadenceArrayValueLength-56] _ = x[MemoryKindCadenceDictionaryValue-57] - _ = x[MemoryKindCadenceKeyValuePair-58] - _ = x[MemoryKindCadenceStructValueBase-59] - _ = x[MemoryKindCadenceStructValueSize-60] - _ = x[MemoryKindCadenceResourceValueBase-61] - _ = x[MemoryKindCadenceAttachmentValueBase-62] - _ = x[MemoryKindCadenceResourceValueSize-63] - _ = x[MemoryKindCadenceAttachmentValueSize-64] - _ = x[MemoryKindCadenceEventValueBase-65] - _ = x[MemoryKindCadenceEventValueSize-66] - _ = x[MemoryKindCadenceContractValueBase-67] - _ = x[MemoryKindCadenceContractValueSize-68] - _ = x[MemoryKindCadenceEnumValueBase-69] - _ = x[MemoryKindCadenceEnumValueSize-70] - _ = x[MemoryKindCadencePathLinkValue-71] - _ = x[MemoryKindCadenceAccountLinkValue-72] - _ = x[MemoryKindCadencePathValue-73] - _ = x[MemoryKindCadenceTypeValue-74] - _ = x[MemoryKindCadenceIDCapabilityValue-75] - _ = x[MemoryKindCadencePathCapabilityValue-76] - _ = x[MemoryKindCadenceFunctionValue-77] - _ = x[MemoryKindCadenceOptionalType-78] - _ = x[MemoryKindCadenceVariableSizedArrayType-79] - _ = x[MemoryKindCadenceConstantSizedArrayType-80] - _ = x[MemoryKindCadenceDictionaryType-81] - _ = x[MemoryKindCadenceInclusiveRangeType-82] - _ = x[MemoryKindCadenceField-83] - _ = x[MemoryKindCadenceParameter-84] - _ = x[MemoryKindCadenceTypeParameter-85] - _ = x[MemoryKindCadenceStructType-86] - _ = x[MemoryKindCadenceResourceType-87] - _ = x[MemoryKindCadenceAttachmentType-88] - _ = x[MemoryKindCadenceEventType-89] - _ = x[MemoryKindCadenceContractType-90] - _ = x[MemoryKindCadenceStructInterfaceType-91] - _ = x[MemoryKindCadenceResourceInterfaceType-92] - _ = x[MemoryKindCadenceContractInterfaceType-93] - _ = x[MemoryKindCadenceFunctionType-94] - _ = x[MemoryKindCadenceReferenceType-95] - _ = x[MemoryKindCadenceRestrictedType-96] - _ = x[MemoryKindCadenceCapabilityType-97] - _ = x[MemoryKindCadenceEnumType-98] - _ = x[MemoryKindRawString-99] - _ = x[MemoryKindAddressLocation-100] - _ = x[MemoryKindBytes-101] - _ = x[MemoryKindVariable-102] - _ = x[MemoryKindCompositeTypeInfo-103] - _ = x[MemoryKindCompositeField-104] - _ = x[MemoryKindInvocation-105] - _ = x[MemoryKindStorageMap-106] - _ = x[MemoryKindStorageKey-107] - _ = x[MemoryKindTypeToken-108] - _ = x[MemoryKindErrorToken-109] - _ = x[MemoryKindSpaceToken-110] - _ = x[MemoryKindProgram-111] - _ = x[MemoryKindIdentifier-112] - _ = x[MemoryKindArgument-113] - _ = x[MemoryKindBlock-114] - _ = x[MemoryKindFunctionBlock-115] - _ = x[MemoryKindParameter-116] - _ = x[MemoryKindParameterList-117] - _ = x[MemoryKindTypeParameter-118] - _ = x[MemoryKindTypeParameterList-119] - _ = x[MemoryKindTransfer-120] - _ = x[MemoryKindMembers-121] - _ = x[MemoryKindTypeAnnotation-122] - _ = x[MemoryKindDictionaryEntry-123] - _ = x[MemoryKindFunctionDeclaration-124] - _ = x[MemoryKindCompositeDeclaration-125] - _ = x[MemoryKindAttachmentDeclaration-126] - _ = x[MemoryKindInterfaceDeclaration-127] - _ = x[MemoryKindEnumCaseDeclaration-128] - _ = x[MemoryKindFieldDeclaration-129] - _ = x[MemoryKindTransactionDeclaration-130] - _ = x[MemoryKindImportDeclaration-131] - _ = x[MemoryKindVariableDeclaration-132] - _ = x[MemoryKindSpecialFunctionDeclaration-133] - _ = x[MemoryKindPragmaDeclaration-134] - _ = x[MemoryKindAssignmentStatement-135] - _ = x[MemoryKindBreakStatement-136] - _ = x[MemoryKindContinueStatement-137] - _ = x[MemoryKindEmitStatement-138] - _ = x[MemoryKindExpressionStatement-139] - _ = x[MemoryKindForStatement-140] - _ = x[MemoryKindIfStatement-141] - _ = x[MemoryKindReturnStatement-142] - _ = x[MemoryKindSwapStatement-143] - _ = x[MemoryKindSwitchStatement-144] - _ = x[MemoryKindWhileStatement-145] - _ = x[MemoryKindRemoveStatement-146] - _ = x[MemoryKindBooleanExpression-147] - _ = x[MemoryKindVoidExpression-148] - _ = x[MemoryKindNilExpression-149] - _ = x[MemoryKindStringExpression-150] - _ = x[MemoryKindIntegerExpression-151] - _ = x[MemoryKindFixedPointExpression-152] - _ = x[MemoryKindArrayExpression-153] - _ = x[MemoryKindDictionaryExpression-154] - _ = x[MemoryKindIdentifierExpression-155] - _ = x[MemoryKindInvocationExpression-156] - _ = x[MemoryKindMemberExpression-157] - _ = x[MemoryKindIndexExpression-158] - _ = x[MemoryKindConditionalExpression-159] - _ = x[MemoryKindUnaryExpression-160] - _ = x[MemoryKindBinaryExpression-161] - _ = x[MemoryKindFunctionExpression-162] - _ = x[MemoryKindCastingExpression-163] - _ = x[MemoryKindCreateExpression-164] - _ = x[MemoryKindDestroyExpression-165] - _ = x[MemoryKindReferenceExpression-166] - _ = x[MemoryKindForceExpression-167] - _ = x[MemoryKindPathExpression-168] - _ = x[MemoryKindAttachExpression-169] - _ = x[MemoryKindConstantSizedType-170] - _ = x[MemoryKindDictionaryType-171] - _ = x[MemoryKindFunctionType-172] - _ = x[MemoryKindInstantiationType-173] - _ = x[MemoryKindNominalType-174] - _ = x[MemoryKindOptionalType-175] - _ = x[MemoryKindReferenceType-176] - _ = x[MemoryKindRestrictedType-177] - _ = x[MemoryKindVariableSizedType-178] - _ = x[MemoryKindPosition-179] - _ = x[MemoryKindRange-180] - _ = x[MemoryKindElaboration-181] - _ = x[MemoryKindActivation-182] - _ = x[MemoryKindActivationEntries-183] - _ = x[MemoryKindVariableSizedSemaType-184] - _ = x[MemoryKindConstantSizedSemaType-185] - _ = x[MemoryKindDictionarySemaType-186] - _ = x[MemoryKindOptionalSemaType-187] - _ = x[MemoryKindRestrictedSemaType-188] - _ = x[MemoryKindReferenceSemaType-189] - _ = x[MemoryKindCapabilitySemaType-190] - _ = x[MemoryKindOrderedMap-191] - _ = x[MemoryKindOrderedMapEntryList-192] - _ = x[MemoryKindOrderedMapEntry-193] - _ = x[MemoryKindLast-194] + _ = x[MemoryKindCadenceInclusiveRangeValue-58] + _ = x[MemoryKindCadenceKeyValuePair-59] + _ = x[MemoryKindCadenceStructValueBase-60] + _ = x[MemoryKindCadenceStructValueSize-61] + _ = x[MemoryKindCadenceResourceValueBase-62] + _ = x[MemoryKindCadenceAttachmentValueBase-63] + _ = x[MemoryKindCadenceResourceValueSize-64] + _ = x[MemoryKindCadenceAttachmentValueSize-65] + _ = x[MemoryKindCadenceEventValueBase-66] + _ = x[MemoryKindCadenceEventValueSize-67] + _ = x[MemoryKindCadenceContractValueBase-68] + _ = x[MemoryKindCadenceContractValueSize-69] + _ = x[MemoryKindCadenceEnumValueBase-70] + _ = x[MemoryKindCadenceEnumValueSize-71] + _ = x[MemoryKindCadencePathLinkValue-72] + _ = x[MemoryKindCadenceAccountLinkValue-73] + _ = x[MemoryKindCadencePathValue-74] + _ = x[MemoryKindCadenceTypeValue-75] + _ = x[MemoryKindCadenceIDCapabilityValue-76] + _ = x[MemoryKindCadencePathCapabilityValue-77] + _ = x[MemoryKindCadenceFunctionValue-78] + _ = x[MemoryKindCadenceOptionalType-79] + _ = x[MemoryKindCadenceVariableSizedArrayType-80] + _ = x[MemoryKindCadenceConstantSizedArrayType-81] + _ = x[MemoryKindCadenceDictionaryType-82] + _ = x[MemoryKindCadenceInclusiveRangeType-83] + _ = x[MemoryKindCadenceField-84] + _ = x[MemoryKindCadenceParameter-85] + _ = x[MemoryKindCadenceTypeParameter-86] + _ = x[MemoryKindCadenceStructType-87] + _ = x[MemoryKindCadenceResourceType-88] + _ = x[MemoryKindCadenceAttachmentType-89] + _ = x[MemoryKindCadenceEventType-90] + _ = x[MemoryKindCadenceContractType-91] + _ = x[MemoryKindCadenceStructInterfaceType-92] + _ = x[MemoryKindCadenceResourceInterfaceType-93] + _ = x[MemoryKindCadenceContractInterfaceType-94] + _ = x[MemoryKindCadenceFunctionType-95] + _ = x[MemoryKindCadenceReferenceType-96] + _ = x[MemoryKindCadenceRestrictedType-97] + _ = x[MemoryKindCadenceCapabilityType-98] + _ = x[MemoryKindCadenceEnumType-99] + _ = x[MemoryKindRawString-100] + _ = x[MemoryKindAddressLocation-101] + _ = x[MemoryKindBytes-102] + _ = x[MemoryKindVariable-103] + _ = x[MemoryKindCompositeTypeInfo-104] + _ = x[MemoryKindCompositeField-105] + _ = x[MemoryKindInvocation-106] + _ = x[MemoryKindStorageMap-107] + _ = x[MemoryKindStorageKey-108] + _ = x[MemoryKindTypeToken-109] + _ = x[MemoryKindErrorToken-110] + _ = x[MemoryKindSpaceToken-111] + _ = x[MemoryKindProgram-112] + _ = x[MemoryKindIdentifier-113] + _ = x[MemoryKindArgument-114] + _ = x[MemoryKindBlock-115] + _ = x[MemoryKindFunctionBlock-116] + _ = x[MemoryKindParameter-117] + _ = x[MemoryKindParameterList-118] + _ = x[MemoryKindTypeParameter-119] + _ = x[MemoryKindTypeParameterList-120] + _ = x[MemoryKindTransfer-121] + _ = x[MemoryKindMembers-122] + _ = x[MemoryKindTypeAnnotation-123] + _ = x[MemoryKindDictionaryEntry-124] + _ = x[MemoryKindFunctionDeclaration-125] + _ = x[MemoryKindCompositeDeclaration-126] + _ = x[MemoryKindAttachmentDeclaration-127] + _ = x[MemoryKindInterfaceDeclaration-128] + _ = x[MemoryKindEnumCaseDeclaration-129] + _ = x[MemoryKindFieldDeclaration-130] + _ = x[MemoryKindTransactionDeclaration-131] + _ = x[MemoryKindImportDeclaration-132] + _ = x[MemoryKindVariableDeclaration-133] + _ = x[MemoryKindSpecialFunctionDeclaration-134] + _ = x[MemoryKindPragmaDeclaration-135] + _ = x[MemoryKindAssignmentStatement-136] + _ = x[MemoryKindBreakStatement-137] + _ = x[MemoryKindContinueStatement-138] + _ = x[MemoryKindEmitStatement-139] + _ = x[MemoryKindExpressionStatement-140] + _ = x[MemoryKindForStatement-141] + _ = x[MemoryKindIfStatement-142] + _ = x[MemoryKindReturnStatement-143] + _ = x[MemoryKindSwapStatement-144] + _ = x[MemoryKindSwitchStatement-145] + _ = x[MemoryKindWhileStatement-146] + _ = x[MemoryKindRemoveStatement-147] + _ = x[MemoryKindBooleanExpression-148] + _ = x[MemoryKindVoidExpression-149] + _ = x[MemoryKindNilExpression-150] + _ = x[MemoryKindStringExpression-151] + _ = x[MemoryKindIntegerExpression-152] + _ = x[MemoryKindFixedPointExpression-153] + _ = x[MemoryKindArrayExpression-154] + _ = x[MemoryKindDictionaryExpression-155] + _ = x[MemoryKindIdentifierExpression-156] + _ = x[MemoryKindInvocationExpression-157] + _ = x[MemoryKindMemberExpression-158] + _ = x[MemoryKindIndexExpression-159] + _ = x[MemoryKindConditionalExpression-160] + _ = x[MemoryKindUnaryExpression-161] + _ = x[MemoryKindBinaryExpression-162] + _ = x[MemoryKindFunctionExpression-163] + _ = x[MemoryKindCastingExpression-164] + _ = x[MemoryKindCreateExpression-165] + _ = x[MemoryKindDestroyExpression-166] + _ = x[MemoryKindReferenceExpression-167] + _ = x[MemoryKindForceExpression-168] + _ = x[MemoryKindPathExpression-169] + _ = x[MemoryKindAttachExpression-170] + _ = x[MemoryKindConstantSizedType-171] + _ = x[MemoryKindDictionaryType-172] + _ = x[MemoryKindFunctionType-173] + _ = x[MemoryKindInstantiationType-174] + _ = x[MemoryKindNominalType-175] + _ = x[MemoryKindOptionalType-176] + _ = x[MemoryKindReferenceType-177] + _ = x[MemoryKindRestrictedType-178] + _ = x[MemoryKindVariableSizedType-179] + _ = x[MemoryKindPosition-180] + _ = x[MemoryKindRange-181] + _ = x[MemoryKindElaboration-182] + _ = x[MemoryKindActivation-183] + _ = x[MemoryKindActivationEntries-184] + _ = x[MemoryKindVariableSizedSemaType-185] + _ = x[MemoryKindConstantSizedSemaType-186] + _ = x[MemoryKindDictionarySemaType-187] + _ = x[MemoryKindOptionalSemaType-188] + _ = x[MemoryKindRestrictedSemaType-189] + _ = x[MemoryKindReferenceSemaType-190] + _ = x[MemoryKindCapabilitySemaType-191] + _ = x[MemoryKindInclusiveRangeSemaType-192] + _ = x[MemoryKindOrderedMap-193] + _ = x[MemoryKindOrderedMapEntryList-194] + _ = x[MemoryKindOrderedMapEntry-195] + _ = x[MemoryKindLast-196] } -const _MemoryKind_name = "UnknownAddressValueStringValueCharacterValueNumberValueArrayValueBaseDictionaryValueBaseCompositeValueBaseSimpleCompositeValueBaseOptionalValueTypeValuePathValueIDCapabilityValuePathCapabilityValuePathLinkValueAccountLinkValueStorageReferenceValueAccountReferenceValueEphemeralReferenceValueInterpretedFunctionValueHostFunctionValueBoundFunctionValueBigIntSimpleCompositeValuePublishedValueStorageCapabilityControllerValueAccountCapabilityControllerValueAtreeArrayDataSlabAtreeArrayMetaDataSlabAtreeArrayElementOverheadAtreeMapDataSlabAtreeMapMetaDataSlabAtreeMapElementOverheadAtreeMapPreAllocatedElementAtreeEncodedSlabPrimitiveStaticTypeCompositeStaticTypeInterfaceStaticTypeVariableSizedStaticTypeConstantSizedStaticTypeDictionaryStaticTypeInclusiveRangeStaticTypeOptionalStaticTypeRestrictedStaticTypeReferenceStaticTypeCapabilityStaticTypeFunctionStaticTypeCadenceVoidValueCadenceOptionalValueCadenceBoolValueCadenceStringValueCadenceCharacterValueCadenceAddressValueCadenceIntValueCadenceNumberValueCadenceArrayValueBaseCadenceArrayValueLengthCadenceDictionaryValueCadenceKeyValuePairCadenceStructValueBaseCadenceStructValueSizeCadenceResourceValueBaseCadenceAttachmentValueBaseCadenceResourceValueSizeCadenceAttachmentValueSizeCadenceEventValueBaseCadenceEventValueSizeCadenceContractValueBaseCadenceContractValueSizeCadenceEnumValueBaseCadenceEnumValueSizeCadencePathLinkValueCadenceAccountLinkValueCadencePathValueCadenceTypeValueCadenceIDCapabilityValueCadencePathCapabilityValueCadenceFunctionValueCadenceOptionalTypeCadenceVariableSizedArrayTypeCadenceConstantSizedArrayTypeCadenceDictionaryTypeCadenceInclusiveRangeTypeCadenceFieldCadenceParameterCadenceTypeParameterCadenceStructTypeCadenceResourceTypeCadenceAttachmentTypeCadenceEventTypeCadenceContractTypeCadenceStructInterfaceTypeCadenceResourceInterfaceTypeCadenceContractInterfaceTypeCadenceFunctionTypeCadenceReferenceTypeCadenceRestrictedTypeCadenceCapabilityTypeCadenceEnumTypeRawStringAddressLocationBytesVariableCompositeTypeInfoCompositeFieldInvocationStorageMapStorageKeyTypeTokenErrorTokenSpaceTokenProgramIdentifierArgumentBlockFunctionBlockParameterParameterListTypeParameterTypeParameterListTransferMembersTypeAnnotationDictionaryEntryFunctionDeclarationCompositeDeclarationAttachmentDeclarationInterfaceDeclarationEnumCaseDeclarationFieldDeclarationTransactionDeclarationImportDeclarationVariableDeclarationSpecialFunctionDeclarationPragmaDeclarationAssignmentStatementBreakStatementContinueStatementEmitStatementExpressionStatementForStatementIfStatementReturnStatementSwapStatementSwitchStatementWhileStatementRemoveStatementBooleanExpressionVoidExpressionNilExpressionStringExpressionIntegerExpressionFixedPointExpressionArrayExpressionDictionaryExpressionIdentifierExpressionInvocationExpressionMemberExpressionIndexExpressionConditionalExpressionUnaryExpressionBinaryExpressionFunctionExpressionCastingExpressionCreateExpressionDestroyExpressionReferenceExpressionForceExpressionPathExpressionAttachExpressionConstantSizedTypeDictionaryTypeFunctionTypeInstantiationTypeNominalTypeOptionalTypeReferenceTypeRestrictedTypeVariableSizedTypePositionRangeElaborationActivationActivationEntriesVariableSizedSemaTypeConstantSizedSemaTypeDictionarySemaTypeOptionalSemaTypeRestrictedSemaTypeReferenceSemaTypeCapabilitySemaTypeOrderedMapOrderedMapEntryListOrderedMapEntryLast" +const _MemoryKind_name = "UnknownAddressValueStringValueCharacterValueNumberValueArrayValueBaseDictionaryValueBaseCompositeValueBaseSimpleCompositeValueBaseOptionalValueTypeValuePathValueIDCapabilityValuePathCapabilityValuePathLinkValueAccountLinkValueStorageReferenceValueAccountReferenceValueEphemeralReferenceValueInterpretedFunctionValueHostFunctionValueBoundFunctionValueBigIntSimpleCompositeValuePublishedValueStorageCapabilityControllerValueAccountCapabilityControllerValueAtreeArrayDataSlabAtreeArrayMetaDataSlabAtreeArrayElementOverheadAtreeMapDataSlabAtreeMapMetaDataSlabAtreeMapElementOverheadAtreeMapPreAllocatedElementAtreeEncodedSlabPrimitiveStaticTypeCompositeStaticTypeInterfaceStaticTypeVariableSizedStaticTypeConstantSizedStaticTypeDictionaryStaticTypeInclusiveRangeStaticTypeOptionalStaticTypeRestrictedStaticTypeReferenceStaticTypeCapabilityStaticTypeFunctionStaticTypeCadenceVoidValueCadenceOptionalValueCadenceBoolValueCadenceStringValueCadenceCharacterValueCadenceAddressValueCadenceIntValueCadenceNumberValueCadenceArrayValueBaseCadenceArrayValueLengthCadenceDictionaryValueCadenceInclusiveRangeValueCadenceKeyValuePairCadenceStructValueBaseCadenceStructValueSizeCadenceResourceValueBaseCadenceAttachmentValueBaseCadenceResourceValueSizeCadenceAttachmentValueSizeCadenceEventValueBaseCadenceEventValueSizeCadenceContractValueBaseCadenceContractValueSizeCadenceEnumValueBaseCadenceEnumValueSizeCadencePathLinkValueCadenceAccountLinkValueCadencePathValueCadenceTypeValueCadenceIDCapabilityValueCadencePathCapabilityValueCadenceFunctionValueCadenceOptionalTypeCadenceVariableSizedArrayTypeCadenceConstantSizedArrayTypeCadenceDictionaryTypeCadenceInclusiveRangeTypeCadenceFieldCadenceParameterCadenceTypeParameterCadenceStructTypeCadenceResourceTypeCadenceAttachmentTypeCadenceEventTypeCadenceContractTypeCadenceStructInterfaceTypeCadenceResourceInterfaceTypeCadenceContractInterfaceTypeCadenceFunctionTypeCadenceReferenceTypeCadenceRestrictedTypeCadenceCapabilityTypeCadenceEnumTypeRawStringAddressLocationBytesVariableCompositeTypeInfoCompositeFieldInvocationStorageMapStorageKeyTypeTokenErrorTokenSpaceTokenProgramIdentifierArgumentBlockFunctionBlockParameterParameterListTypeParameterTypeParameterListTransferMembersTypeAnnotationDictionaryEntryFunctionDeclarationCompositeDeclarationAttachmentDeclarationInterfaceDeclarationEnumCaseDeclarationFieldDeclarationTransactionDeclarationImportDeclarationVariableDeclarationSpecialFunctionDeclarationPragmaDeclarationAssignmentStatementBreakStatementContinueStatementEmitStatementExpressionStatementForStatementIfStatementReturnStatementSwapStatementSwitchStatementWhileStatementRemoveStatementBooleanExpressionVoidExpressionNilExpressionStringExpressionIntegerExpressionFixedPointExpressionArrayExpressionDictionaryExpressionIdentifierExpressionInvocationExpressionMemberExpressionIndexExpressionConditionalExpressionUnaryExpressionBinaryExpressionFunctionExpressionCastingExpressionCreateExpressionDestroyExpressionReferenceExpressionForceExpressionPathExpressionAttachExpressionConstantSizedTypeDictionaryTypeFunctionTypeInstantiationTypeNominalTypeOptionalTypeReferenceTypeRestrictedTypeVariableSizedTypePositionRangeElaborationActivationActivationEntriesVariableSizedSemaTypeConstantSizedSemaTypeDictionarySemaTypeOptionalSemaTypeRestrictedSemaTypeReferenceSemaTypeCapabilitySemaTypeInclusiveRangeSemaTypeOrderedMapOrderedMapEntryListOrderedMapEntryLast" -var _MemoryKind_index = [...]uint16{0, 7, 19, 30, 44, 55, 69, 88, 106, 130, 143, 152, 161, 178, 197, 210, 226, 247, 268, 291, 315, 332, 350, 356, 376, 390, 422, 454, 472, 494, 519, 535, 555, 578, 605, 621, 640, 659, 678, 701, 724, 744, 768, 786, 806, 825, 845, 863, 879, 899, 915, 933, 954, 973, 988, 1006, 1027, 1050, 1072, 1091, 1113, 1135, 1159, 1185, 1209, 1235, 1256, 1277, 1301, 1325, 1345, 1365, 1385, 1408, 1424, 1440, 1464, 1490, 1510, 1529, 1558, 1587, 1608, 1633, 1645, 1661, 1681, 1698, 1717, 1738, 1754, 1773, 1799, 1827, 1855, 1874, 1894, 1915, 1936, 1951, 1960, 1975, 1980, 1988, 2005, 2019, 2029, 2039, 2049, 2058, 2068, 2078, 2085, 2095, 2103, 2108, 2121, 2130, 2143, 2156, 2173, 2181, 2188, 2202, 2217, 2236, 2256, 2277, 2297, 2316, 2332, 2354, 2371, 2390, 2416, 2433, 2452, 2466, 2483, 2496, 2515, 2527, 2538, 2553, 2566, 2581, 2595, 2610, 2627, 2641, 2654, 2670, 2687, 2707, 2722, 2742, 2762, 2782, 2798, 2813, 2834, 2849, 2865, 2883, 2900, 2916, 2933, 2952, 2967, 2981, 2997, 3014, 3028, 3040, 3057, 3068, 3080, 3093, 3107, 3124, 3132, 3137, 3148, 3158, 3175, 3196, 3217, 3235, 3251, 3269, 3286, 3304, 3314, 3333, 3348, 3352} +var _MemoryKind_index = [...]uint16{0, 7, 19, 30, 44, 55, 69, 88, 106, 130, 143, 152, 161, 178, 197, 210, 226, 247, 268, 291, 315, 332, 350, 356, 376, 390, 422, 454, 472, 494, 519, 535, 555, 578, 605, 621, 640, 659, 678, 701, 724, 744, 768, 786, 806, 825, 845, 863, 879, 899, 915, 933, 954, 973, 988, 1006, 1027, 1050, 1072, 1098, 1117, 1139, 1161, 1185, 1211, 1235, 1261, 1282, 1303, 1327, 1351, 1371, 1391, 1411, 1434, 1450, 1466, 1490, 1516, 1536, 1555, 1584, 1613, 1634, 1659, 1671, 1687, 1707, 1724, 1743, 1764, 1780, 1799, 1825, 1853, 1881, 1900, 1920, 1941, 1962, 1977, 1986, 2001, 2006, 2014, 2031, 2045, 2055, 2065, 2075, 2084, 2094, 2104, 2111, 2121, 2129, 2134, 2147, 2156, 2169, 2182, 2199, 2207, 2214, 2228, 2243, 2262, 2282, 2303, 2323, 2342, 2358, 2380, 2397, 2416, 2442, 2459, 2478, 2492, 2509, 2522, 2541, 2553, 2564, 2579, 2592, 2607, 2621, 2636, 2653, 2667, 2680, 2696, 2713, 2733, 2748, 2768, 2788, 2808, 2824, 2839, 2860, 2875, 2891, 2909, 2926, 2942, 2959, 2978, 2993, 3007, 3023, 3040, 3054, 3066, 3083, 3094, 3106, 3119, 3133, 3150, 3158, 3163, 3174, 3184, 3201, 3222, 3243, 3261, 3277, 3295, 3312, 3330, 3352, 3362, 3381, 3396, 3400} func (i MemoryKind) String() string { if i >= MemoryKind(len(_MemoryKind_index)-1) { diff --git a/runtime/common/metering.go b/runtime/common/metering.go index c3b41e1bd6..974249e509 100644 --- a/runtime/common/metering.go +++ b/runtime/common/metering.go @@ -174,13 +174,14 @@ var ( // Sema types - VariableSizedSemaTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindVariableSizedSemaType) - ConstantSizedSemaTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindConstantSizedSemaType) - DictionarySemaTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindDictionarySemaType) - OptionalSemaTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindOptionalSemaType) - RestrictedSemaTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindRestrictedSemaType) - ReferenceSemaTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindReferenceSemaType) - CapabilitySemaTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindCapabilitySemaType) + VariableSizedSemaTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindVariableSizedSemaType) + ConstantSizedSemaTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindConstantSizedSemaType) + DictionarySemaTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindDictionarySemaType) + InclusiveRangeSemaTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindInclusiveRangeSemaType) + OptionalSemaTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindOptionalSemaType) + RestrictedSemaTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindRestrictedSemaType) + ReferenceSemaTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindReferenceSemaType) + CapabilitySemaTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindCapabilitySemaType) // Storage related memory usages @@ -192,6 +193,7 @@ var ( // Cadence external values CadenceDictionaryValueMemoryUsage = NewConstantMemoryUsage(MemoryKindCadenceDictionaryValue) + CadenceInclusiveRangeValueMemoryUsage = NewConstantMemoryUsage(MemoryKindCadenceInclusiveRangeValue) CadenceArrayValueBaseMemoryUsage = NewConstantMemoryUsage(MemoryKindCadenceArrayValueBase) CadenceStructValueBaseMemoryUsage = NewConstantMemoryUsage(MemoryKindCadenceStructValueBase) CadenceResourceValueBaseMemoryUsage = NewConstantMemoryUsage(MemoryKindCadenceResourceValueBase) diff --git a/runtime/convertValues.go b/runtime/convertValues.go index 82a2caa0c7..82b7dd09c3 100644 --- a/runtime/convertValues.go +++ b/runtime/convertValues.go @@ -382,7 +382,13 @@ func exportCompositeValue( compositeType, ok := semaType.(*sema.CompositeType) if !ok { - panic(errors.NewUnreachableError()) + // InclusiveRange is stored as a CompositeValue but isn't a CompositeType. + inclusiveRangeType, ok := semaType.(*sema.InclusiveRangeType) + if !ok { + panic(errors.NewUnreachableError()) + } + + return exportCompositeValueAsInclusiveRange(v, inclusiveRangeType, inter, locationRange, seenReferences) } // TODO: consider making the results map "global", by moving it up to exportValueWithInterpreter @@ -613,6 +619,67 @@ func exportDictionaryValue( return dictionary.WithType(exportType), err } +func exportCompositeValueAsInclusiveRange( + v interpreter.Value, + inclusiveRangeType *sema.InclusiveRangeType, + inter *interpreter.Interpreter, + locationRange interpreter.LocationRange, + seenReferences seenReferences, +) ( + cadence.InclusiveRange, + error, +) { + compositeValue, ok := v.(*interpreter.CompositeValue) + if !ok { + // InclusiveRange is stored as a CompositeValue. + panic(errors.NewUnreachableError()) + } + + getField := func(fieldName string) (cadence.Value, error) { + fieldValue := compositeValue.GetField(inter, locationRange, fieldName) + if fieldValue == nil && compositeValue.ComputedFields != nil { + if computedField, ok := compositeValue.ComputedFields[fieldName]; ok { + fieldValue = computedField(inter, locationRange) + } + } + + return exportValueWithInterpreter( + fieldValue, + inter, + locationRange, + seenReferences, + ) + } + + startValue, err := getField(sema.InclusiveRangeTypeStartFieldName) + if err != nil { + return cadence.InclusiveRange{}, err + } + + endValue, err := getField(sema.InclusiveRangeTypeEndFieldName) + if err != nil { + return cadence.InclusiveRange{}, err + } + + stepValue, err := getField(sema.InclusiveRangeTypeStepFieldName) + if err != nil { + return cadence.InclusiveRange{}, err + } + + inclusiveRange, err := cadence.NewMeteredInclusiveRange( + inter, + startValue, + endValue, + stepValue, + ) + if err != nil { + return cadence.InclusiveRange{}, err + } + + t := exportInclusiveRangeType(inter, inclusiveRangeType, map[sema.TypeID]cadence.Type{}).(*cadence.InclusiveRangeType) + return inclusiveRange.WithType(t), err +} + func exportPathLinkValue(v interpreter.PathLinkValue, inter *interpreter.Interpreter) (cadence.PathLink, error) { path, err := exportPathValue(inter, v.TargetPath) if err != nil { @@ -853,6 +920,8 @@ func (i valueImporter) importValue(value cadence.Value, expectedType sema.Type) v.EnumType.Fields, v.Fields, ) + case cadence.InclusiveRange: + return i.importInclusiveRangeValue(v, expectedType) case cadence.TypeValue: return i.importTypeValue(v.StaticType) case cadence.PathCapability: @@ -1382,6 +1451,69 @@ func (i valueImporter) importDictionaryValue( ), nil } +func (i valueImporter) importInclusiveRangeValue( + v cadence.InclusiveRange, + expectedType sema.Type, +) ( + *interpreter.CompositeValue, + error, +) { + + inclusiveRangeType, ok := expectedType.(*sema.InclusiveRangeType) + if !ok { + return nil, errors.NewDefaultUserError( + "cannot import InclusiveRange: expected InclusiveRangeType, got '%s'", + expectedType.ID(), + ) + } + + expectedMemberType := inclusiveRangeType.MemberType + inter := i.inter + locationRange := i.locationRange + + startValue, err := i.importValue(v.Start, expectedMemberType) + if err != nil { + return nil, err + } + startInteger, ok := startValue.(interpreter.IntegerValue) + if !ok { + return nil, err + } + + stepValue, err := i.importValue(v.Step, expectedMemberType) + if err != nil { + return nil, err + } + stepInteger, ok := stepValue.(interpreter.IntegerValue) + if !ok { + return nil, err + } + + endValue, err := i.importValue(v.End, expectedMemberType) + if err != nil { + return nil, err + } + endInteger, ok := endValue.(interpreter.IntegerValue) + if !ok { + return nil, err + } + + staticType := interpreter.ConvertSemaToStaticType(inter, inclusiveRangeType) + inclusiveRangeStaticType, ok := staticType.(interpreter.InclusiveRangeStaticType) + if !ok { + panic(errors.NewUnreachableError()) + } + + return interpreter.NewInclusiveRangeValueWithStep( + inter, + locationRange, + startInteger, + endInteger, + stepInteger, + inclusiveRangeStaticType, + ), nil +} + func (i valueImporter) importCompositeValue( kind common.CompositeKind, location Location, diff --git a/runtime/convertValues_test.go b/runtime/convertValues_test.go index 9aa89660d2..3f5fa78378 100644 --- a/runtime/convertValues_test.go +++ b/runtime/convertValues_test.go @@ -1441,6 +1441,91 @@ func TestExportAddressValue(t *testing.T) { assert.Equal(t, expected, actual) } +func TestExportInclusiveRangeValue(t *testing.T) { + + t.Parallel() + + t.Run("with_step", func(t *testing.T) { + + t.Parallel() + + script := ` + pub fun main(): InclusiveRange { + return InclusiveRange(10, 20, step: 2) + } + ` + + inclusiveRangeType := cadence.NewInclusiveRangeType(cadence.IntType{}) + + actual := cadence.ValueWithCachedTypeID(exportValueFromScript(t, script)) + expected := cadence.ValueWithCachedTypeID( + cadence.NewInclusiveRange( + cadence.NewInt(10), + cadence.NewInt(20), + cadence.NewInt(2)).WithType(inclusiveRangeType), + ) + + assert.Equal(t, expected, actual) + }) + + t.Run("without_step", func(t *testing.T) { + + t.Parallel() + + script := ` + pub fun main(): InclusiveRange { + return InclusiveRange(10, 20) + } + ` + + inclusiveRangeType := cadence.NewInclusiveRangeType(cadence.IntType{}) + + actual := cadence.ValueWithCachedTypeID(exportValueFromScript(t, script)) + expected := cadence.ValueWithCachedTypeID( + cadence.NewInclusiveRange( + cadence.NewInt(10), + cadence.NewInt(20), + cadence.NewInt(1)).WithType(inclusiveRangeType), + ) + + assert.Equal(t, expected, actual) + }) +} + +func TestImportInclusiveRangeValue(t *testing.T) { + + t.Parallel() + + value := cadence.NewInclusiveRange(cadence.NewInt(10), cadence.NewInt(-10), cadence.NewInt(-2)) + + inter := newTestInterpreter(t) + + actual, err := ImportValue( + inter, + interpreter.EmptyLocationRange, + nil, + value, + sema.NewInclusiveRangeType(inter, sema.IntType), + ) + require.NoError(t, err) + + AssertValuesEqual( + t, + inter, + interpreter.NewInclusiveRangeValueWithStep( + inter, + interpreter.EmptyLocationRange, + interpreter.NewIntValueFromInt64(inter, 10), + interpreter.NewIntValueFromInt64(inter, -10), + interpreter.NewIntValueFromInt64(inter, -2), + interpreter.InclusiveRangeStaticType{ + ElementType: interpreter.PrimitiveStaticTypeInt, + }, + ), + actual, + ) +} + func TestExportStructValue(t *testing.T) { t.Parallel() diff --git a/runtime/interpreter/interpreter.go b/runtime/interpreter/interpreter.go index a11bf43ec5..5cd38a3157 100644 --- a/runtime/interpreter/interpreter.go +++ b/runtime/interpreter/interpreter.go @@ -3622,6 +3622,36 @@ var runtimeTypeConstructors = []runtimeTypeConstructor{ }, ), }, + { + name: "InclusiveRangeType", + converter: NewUnmeteredHostFunctionValue( + sema.InclusiveRangeTypeFunctionType, + func(invocation Invocation) Value { + typeValue, ok := invocation.Arguments[0].(TypeValue) + if !ok { + panic(errors.NewUnreachableError()) + } + + ty := typeValue.Type + // InclusiveRanges must hold integers + // _, ok = ty.(Sig) + // if !ok { + // return Nil + // } + + return NewSomeValueNonCopying( + invocation.Interpreter, + NewTypeValue( + invocation.Interpreter, + NewInclusiveRangeStaticType( + invocation.Interpreter, + ty, + ), + ), + ) + }, + ), + }, } func defineRuntimeTypeConstructorFunctions(activation *VariableActivation) { diff --git a/runtime/interpreter/statictype.go b/runtime/interpreter/statictype.go index 2438c891b9..95879481e1 100644 --- a/runtime/interpreter/statictype.go +++ b/runtime/interpreter/statictype.go @@ -709,6 +709,10 @@ func ConvertSemaToStaticType(memoryGauge common.MemoryGauge, t sema.Type) Static borrowType := ConvertSemaToStaticType(memoryGauge, t.BorrowType) return NewCapabilityStaticType(memoryGauge, borrowType) + case *sema.InclusiveRangeType: + memberType := ConvertSemaToStaticType(memoryGauge, t.MemberType) + return NewInclusiveRangeStaticType(memoryGauge, memberType) + case *sema.FunctionType: return NewFunctionStaticType(memoryGauge, t) } diff --git a/runtime/interpreter/storage_test.go b/runtime/interpreter/storage_test.go index b97cbc784a..28800e539e 100644 --- a/runtime/interpreter/storage_test.go +++ b/runtime/interpreter/storage_test.go @@ -86,6 +86,61 @@ func TestCompositeStorage(t *testing.T) { ) } +func TestInclusiveRangeStorage(t *testing.T) { + + t.Parallel() + + storage := newUnmeteredInMemoryStorage() + + inter, err := NewInterpreter( + nil, + common.AddressLocation{}, + &Config{Storage: storage}, + ) + require.NoError(t, err) + + value := NewInclusiveRangeValueWithStep( + inter, + EmptyLocationRange, + NewUnmeteredInt16Value(1), + NewUnmeteredInt16Value(100), + NewUnmeteredInt16Value(5), + NewInclusiveRangeStaticType(inter, PrimitiveStaticTypeInt16), + ) + + require.NotEqual(t, atree.StorageIDUndefined, value.StorageID()) + + require.Equal(t, 1, storage.BasicSlabStorage.Count()) + + _, ok, err := storage.BasicSlabStorage.Retrieve(value.StorageID()) + require.NoError(t, err) + require.True(t, ok) + + // Ensure that updating a field (e.g. step) works + const stepFieldName = "step" + + value.SetMember(inter, EmptyLocationRange, stepFieldName, NewUnmeteredInt16Value(10)) + + require.Equal(t, 1, storage.BasicSlabStorage.Count()) + + retrievedStorable, ok, err := storage.BasicSlabStorage.Retrieve(value.StorageID()) + require.NoError(t, err) + require.True(t, ok) + + storedValue := StoredValue(inter, retrievedStorable, storage) + + // InclusiveRange is stored as a CompositeValue. + require.IsType(t, storedValue, &CompositeValue{}) + storedComposite := storedValue.(*CompositeValue) + + RequireValuesEqual( + t, + inter, + NewUnmeteredInt16Value(10), + storedComposite.GetField(inter, EmptyLocationRange, stepFieldName), + ) +} + func TestArrayStorage(t *testing.T) { t.Parallel() diff --git a/runtime/interpreter/value_range.go b/runtime/interpreter/value_range.go index 211a56f0dc..b5b8ba7480 100644 --- a/runtime/interpreter/value_range.go +++ b/runtime/interpreter/value_range.go @@ -35,13 +35,13 @@ func NewInclusiveRangeValue( rangeType InclusiveRangeStaticType, ) *CompositeValue { startComparable, startOk := start.(ComparableValue) - endInclusiveComparable, endInclusiveOk := end.(ComparableValue) - if !startOk || !endInclusiveOk { + endComparable, endOk := end.(ComparableValue) + if !startOk || !endOk { panic(errors.NewUnreachableError()) } step := interpreter.GetValueForIntegerType(1, rangeType.ElementType) - if startComparable.Greater(interpreter, endInclusiveComparable, locationRange) { + if startComparable.Greater(interpreter, endComparable, locationRange) { elemSemaTy := interpreter.MustConvertStaticToSemaType(rangeType.ElementType) if _, ok := sema.AllUnsignedIntegerTypesSet[elemSemaTy]; ok { panic(InclusiveRangeConstructionError{ @@ -71,8 +71,10 @@ func NewInclusiveRangeValueWithStep( rangeType InclusiveRangeStaticType, ) *CompositeValue { + zeroValue := interpreter.GetValueForIntegerType(0, rangeType.ElementType) + // Validate that the step is non-zero. - if step.Equal(interpreter, locationRange, interpreter.GetValueForIntegerType(0, rangeType.ElementType)) { + if step.Equal(interpreter, locationRange, zeroValue) { panic(InclusiveRangeConstructionError{ LocationRange: locationRange, Message: "step value cannot be zero", @@ -83,8 +85,8 @@ func NewInclusiveRangeValueWithStep( // If start < end, step must be > 0 // If start > end, step must be < 0 // If start == end, step doesn't matter. - if (start.Less(interpreter, end, locationRange) && step.Less(interpreter, interpreter.GetValueForIntegerType(0, rangeType.ElementType), locationRange)) || - (start.Greater(interpreter, end, locationRange) && step.Greater(interpreter, interpreter.GetValueForIntegerType(0, rangeType.ElementType), locationRange)) { + if (start.Less(interpreter, end, locationRange) && step.Less(interpreter, zeroValue, locationRange)) || + (start.Greater(interpreter, end, locationRange) && step.Greater(interpreter, zeroValue, locationRange)) { panic(InclusiveRangeConstructionError{ LocationRange: locationRange, @@ -160,24 +162,24 @@ func rangeContains( needleValue IntegerValue, ) BoolValue { start := getFieldAsIntegerValue(rangeValue, interpreter, locationRange, sema.InclusiveRangeTypeStartFieldName) - endInclusive := getFieldAsIntegerValue(rangeValue, interpreter, locationRange, sema.InclusiveRangeTypeEndFieldName) + end := getFieldAsIntegerValue(rangeValue, interpreter, locationRange, sema.InclusiveRangeTypeEndFieldName) step := getFieldAsIntegerValue(rangeValue, interpreter, locationRange, sema.InclusiveRangeTypeStepFieldName) result := start.Equal(interpreter, locationRange, needleValue) || - endInclusive.Equal(interpreter, locationRange, needleValue) + end.Equal(interpreter, locationRange, needleValue) if result { return TrueValue } greaterThanStart := needleValue.Greater(interpreter, start, locationRange) - greaterThanEndInclusive := needleValue.Greater(interpreter, endInclusive, locationRange) + greaterThanend := needleValue.Greater(interpreter, end, locationRange) - if greaterThanStart == greaterThanEndInclusive { - // If needle is greater or smaller than both start & endInclusive, then it is outside the range. + if greaterThanStart == greaterThanend { + // If needle is greater or smaller than both start & end, then it is outside the range. result = false } else { - // needle is in between start and endInclusive. + // needle is in between start and end. // start + k * step should be equal to needle i.e. (needle - start) mod step == 0. diff, ok := needleValue.Minus(interpreter, start, locationRange).(IntegerValue) if !ok { diff --git a/runtime/sema/runtime_type_constructors.go b/runtime/sema/runtime_type_constructors.go index 9dfed98067..dbea3acbb9 100644 --- a/runtime/sema/runtime_type_constructors.go +++ b/runtime/sema/runtime_type_constructors.go @@ -181,6 +181,21 @@ var CapabilityTypeFunctionType = &FunctionType{ ), } +var InclusiveRangeTypeFunctionType = &FunctionType{ + Parameters: []Parameter{ + { + Label: ArgumentLabelNotRequired, + Identifier: "type", + TypeAnnotation: NewTypeAnnotation(MetaType), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + &OptionalType{ + Type: MetaType, + }, + ), +} + var runtimeTypeConstructors = []*RuntimeTypeConstructor{ { Name: "OptionalType", @@ -245,4 +260,11 @@ var runtimeTypeConstructors = []*RuntimeTypeConstructor{ Value: CapabilityTypeFunctionType, DocString: "Creates a run-time type representing a capability type of the given reference type. Returns nil if the type is not a reference.", }, + + { + Name: "InclusiveRangeType", + Value: InclusiveRangeTypeFunctionType, + DocString: `Creates a run-time type representing an inclusive range type of the given run-time member type. + Returns nil if the member type is not a valid inclusive range member type.`, + }, } diff --git a/runtime/sema/type.go b/runtime/sema/type.go index 577a07cf60..5f0d4596c8 100644 --- a/runtime/sema/type.go +++ b/runtime/sema/type.go @@ -3371,6 +3371,7 @@ func init() { HashAlgorithmType, StorageCapabilityControllerType, AccountCapabilityControllerType, + &InclusiveRangeType{}, }, ) @@ -5238,6 +5239,7 @@ type InclusiveRangeType struct { } var _ Type = &InclusiveRangeType{} +var _ ParameterizedType = &InclusiveRangeType{} func NewInclusiveRangeType(memoryGauge common.MemoryGauge, elementType Type) *InclusiveRangeType { common.UseMemory(memoryGauge, common.DictionarySemaTypeMemoryUsage) @@ -5253,9 +5255,13 @@ func (*InclusiveRangeType) Tag() TypeTag { } func (r *InclusiveRangeType) String() string { + memberString := "" + if r.MemberType != nil { + memberString = fmt.Sprintf("<%s>", r.MemberType.String()) + } return fmt.Sprintf( - "InclusiveRange<%s>", - r.MemberType, + "InclusiveRange%s", + memberString, ) } @@ -5330,6 +5336,41 @@ func (r *InclusiveRangeType) RewriteWithRestrictedTypes() (Type, bool) { } } +func (t *InclusiveRangeType) BaseType() Type { + if t.MemberType == nil { + return nil + } + return &InclusiveRangeType{} +} + +func (t *InclusiveRangeType) Instantiate(typeArguments []Type, report func(err error)) Type { + memberType := typeArguments[0] + return &InclusiveRangeType{ + MemberType: memberType, + } +} + +func (t *InclusiveRangeType) TypeArguments() []Type { + memberType := t.MemberType + if memberType == nil { + memberType = IntegerType + } + return []Type{ + memberType, + } +} + +var inclusiveRangeTypeParameter = &TypeParameter{ + Name: "T", + TypeBound: IntegerType, +} + +func (*InclusiveRangeType) TypeParameters() []*TypeParameter { + return []*TypeParameter{ + inclusiveRangeTypeParameter, + } +} + const InclusiveRangeTypeStartFieldName = "start" const inclusiveRangeTypeStartFieldDocString = ` The start of the InclusiveRange sequence @@ -5980,6 +6021,14 @@ func checkSubTypeWithoutEquality(subType Type, superType Type) bool { return IsSubType(typedSubType.KeyType, typedSuperType.KeyType) && IsSubType(typedSubType.ValueType, typedSuperType.ValueType) + // case *InclusiveRangeType: + // typedSubType, ok := subType.(*InclusiveRangeType) + // if !ok { + // return false + // } + + // return IsSubType(typedSubType.MemberType, typedSuperType.MemberType) + case *VariableSizedType: typedSubType, ok := subType.(*VariableSizedType) if !ok { diff --git a/runtime/sema/type_tags.go b/runtime/sema/type_tags.go index 73d16dde0f..37f0ed2530 100644 --- a/runtime/sema/type_tags.go +++ b/runtime/sema/type_tags.go @@ -329,18 +329,18 @@ var ( Or(CapabilityPathTypeTag). Or(StoragePathTypeTag) - ConstantSizedTypeTag = newTypeTagFromLowerMask(constantSizedTypeMask) - VariableSizedTypeTag = newTypeTagFromLowerMask(variableSizedTypeMask) - DictionaryTypeTag = newTypeTagFromLowerMask(dictionaryTypeMask) - InclusiveRangeTypeTag = newTypeTagFromUpperMask(inclusiveRangeTypeMask) - CompositeTypeTag = newTypeTagFromLowerMask(compositeTypeMask) - ReferenceTypeTag = newTypeTagFromLowerMask(referenceTypeMask) - GenericTypeTag = newTypeTagFromLowerMask(genericTypeMask) - FunctionTypeTag = newTypeTagFromUpperMask(functionTypeMask) - InterfaceTypeTag = newTypeTagFromUpperMask(interfaceTypeMask) + ConstantSizedTypeTag = newTypeTagFromLowerMask(constantSizedTypeMask) + VariableSizedTypeTag = newTypeTagFromLowerMask(variableSizedTypeMask) + DictionaryTypeTag = newTypeTagFromLowerMask(dictionaryTypeMask) + CompositeTypeTag = newTypeTagFromLowerMask(compositeTypeMask) + ReferenceTypeTag = newTypeTagFromLowerMask(referenceTypeMask) + GenericTypeTag = newTypeTagFromLowerMask(genericTypeMask) + FunctionTypeTag = newTypeTagFromUpperMask(functionTypeMask) + InterfaceTypeTag = newTypeTagFromUpperMask(interfaceTypeMask) RestrictedTypeTag = newTypeTagFromUpperMask(restrictedTypeMask) CapabilityTypeTag = newTypeTagFromUpperMask(capabilityTypeMask) + InclusiveRangeTypeTag = newTypeTagFromUpperMask(inclusiveRangeTypeMask) InvalidTypeTag = newTypeTagFromUpperMask(invalidTypeMask) TransactionTypeTag = newTypeTagFromUpperMask(transactionTypeMask) AnyResourceAttachmentTypeTag = newTypeTagFromUpperMask(anyResourceAttachmentMask) @@ -371,7 +371,8 @@ var ( Or(CapabilityTypeTag). Or(FunctionTypeTag). Or(StorageCapabilityControllerTypeTag). - Or(AccountCapabilityControllerTypeTag) + Or(AccountCapabilityControllerTypeTag). + Or(InclusiveRangeTypeTag) AnyResourceTypeTag = newTypeTagFromLowerMask(anyResourceTypeMask). Or(AnyResourceAttachmentTypeTag) diff --git a/runtime/stdlib/range.go b/runtime/stdlib/range.go index 29d4a1b512..b47824d07a 100644 --- a/runtime/stdlib/range.go +++ b/runtime/stdlib/range.go @@ -19,6 +19,8 @@ package stdlib import ( + "fmt" + "github.com/onflow/cadence/runtime/errors" "github.com/onflow/cadence/runtime/interpreter" "github.com/onflow/cadence/runtime/sema" @@ -57,7 +59,7 @@ var inclusiveRangeConstructorFunctionType = func() *sema.FunctionType { }, { Label: sema.ArgumentLabelNotRequired, - Identifier: "endInclusive", + Identifier: "end", TypeAnnotation: typeAnnotation, }, { @@ -80,23 +82,27 @@ var InclusiveRangeConstructorFunction = NewStandardLibraryFunction( inclusiveRangeConstructorFunctionDocString, func(invocation interpreter.Invocation) interpreter.Value { start, startOk := invocation.Arguments[0].(interpreter.IntegerValue) - endInclusive, endInclusiveOk := invocation.Arguments[1].(interpreter.IntegerValue) + end, endOk := invocation.Arguments[1].(interpreter.IntegerValue) - if !startOk || !endInclusiveOk { + if !startOk || !endOk { panic(errors.NewUnreachableError()) } inter := invocation.Interpreter locationRange := invocation.LocationRange - leftStaticType := start.StaticType(inter) - rightStaticType := endInclusive.StaticType(inter) - if leftStaticType != rightStaticType { - // Checker would only allow same type for both start & endInclusive. - panic(errors.NewUnreachableError()) + startStaticType := start.StaticType(inter) + endStaticType := end.StaticType(inter) + if startStaticType != endStaticType { + panic(interpreter.InclusiveRangeConstructionError{ + LocationRange: locationRange, + Message: fmt.Sprintf("start and end are of different types. start: %s and end: %s", + startStaticType, + endStaticType), + }) } - rangeStaticType := interpreter.InclusiveRangeStaticType{ElementType: leftStaticType} + rangeStaticType := interpreter.InclusiveRangeStaticType{ElementType: startStaticType} if len(invocation.Arguments) > 2 { step, ok := invocation.Arguments[2].(interpreter.IntegerValue) @@ -104,16 +110,26 @@ var InclusiveRangeConstructorFunction = NewStandardLibraryFunction( panic(errors.NewUnreachableError()) } + stepStaticType := step.StaticType(inter) + if stepStaticType != startStaticType { + panic(interpreter.InclusiveRangeConstructionError{ + LocationRange: locationRange, + Message: fmt.Sprintf("step must be of the same type as start and end. start/end: %s and step: %s", + startStaticType, + stepStaticType), + }) + } + return interpreter.NewInclusiveRangeValueWithStep( inter, locationRange, start, - endInclusive, + end, step, rangeStaticType, ) } else { - return interpreter.NewInclusiveRangeValue(inter, locationRange, start, endInclusive, rangeStaticType) + return interpreter.NewInclusiveRangeValue(inter, locationRange, start, end, rangeStaticType) } }, ) diff --git a/runtime/tests/checker/range_value_test.go b/runtime/tests/checker/range_value_test.go index 7f5be159da..ea4b77e566 100644 --- a/runtime/tests/checker/range_value_test.go +++ b/runtime/tests/checker/range_value_test.go @@ -221,27 +221,27 @@ func TestInclusiveRangeConstructionValid(t *testing.T) { let s : %s = %d let e : %s = %d let step : %s = %d - let r = InclusiveRange(s, e, step: step) + let r: InclusiveRange<%s> = InclusiveRange(s, e, step: step) let rs = r.start let re = r.end let rstep = r.step let contains_res = r.contains(s) `, - testCase.ty.String(), testCase.s, testCase.ty.String(), testCase.e, testCase.ty.String(), testCase.step) + testCase.ty.String(), testCase.s, testCase.ty.String(), testCase.e, testCase.ty.String(), testCase.step, testCase.ty.String()) } else { code = fmt.Sprintf( ` let s : %s = %d let e : %s = %d - let r = InclusiveRange(s, e) + let r: InclusiveRange<%s> = InclusiveRange(s, e) let rs = r.start let re = r.end let rstep = r.step let contains_res = r.contains(s) `, - testCase.ty.String(), testCase.s, testCase.ty.String(), testCase.e) + testCase.ty.String(), testCase.s, testCase.ty.String(), testCase.e, testCase.ty.String()) } checker, err := ParseAndCheckWithOptions(t, code, diff --git a/runtime/tests/interpreter/range_value_test.go b/runtime/tests/interpreter/range_value_test.go index c84a88760f..41e8fabbbd 100644 --- a/runtime/tests/interpreter/range_value_test.go +++ b/runtime/tests/interpreter/range_value_test.go @@ -361,7 +361,7 @@ func TestInclusiveRange(t *testing.T) { let s : %s = %d let e : %s = %d let step : %s = %d - let r = InclusiveRange(s, e, step: step) + let r: InclusiveRange<%s> = InclusiveRange(s, e, step: step) %s `, @@ -371,6 +371,7 @@ func TestInclusiveRange(t *testing.T) { testCase.e, testCase.ty.String(), testCase.step, + testCase.ty.String(), containsCode, ) } else { @@ -562,4 +563,28 @@ func TestInclusiveRangeConstructionInvalid(t *testing.T) { "step value cannot be negative for unsigned integer type", ) } + + runInvalidCase( + t, + "same_supertype_different_subtype_start_end", + ` + let a: Integer = UInt8(0) + let b: Integer = Int16(10) + let r = InclusiveRange(a, b) + `, + &interpreter.InclusiveRangeConstructionError{}, + "start and end are of different types", + ) + runInvalidCase( + t, + "same_supertype_different_subtype_start_step", + ` + let a: Integer = UInt8(0) + let b: Integer = UInt8(10) + let s: Integer = UInt16(2) + let r = InclusiveRange(a, b, step: s) + `, + &interpreter.InclusiveRangeConstructionError{}, + "step must be of the same type as start and end", + ) } diff --git a/values.go b/values.go index d091806866..bda8a7e431 100644 --- a/values.go +++ b/values.go @@ -2051,6 +2051,69 @@ func (v Contract) GetFieldValues() []Value { return v.Fields } +// InclusiveRange + +type InclusiveRange struct { + InclusiveRangeType *InclusiveRangeType + Start Value + End Value + Step Value +} + +var _ Value = InclusiveRange{} + +func NewInclusiveRange(start, end, step Value) InclusiveRange { + return InclusiveRange{ + Start: start, + End: end, + Step: step, + } +} + +func NewMeteredInclusiveRange( + gauge common.MemoryGauge, + start, end, step Value, +) (InclusiveRange, error) { + common.UseMemory(gauge, common.CadenceInclusiveRangeValueMemoryUsage) + return NewInclusiveRange(start, end, step), nil +} + +func (InclusiveRange) isValue() {} + +func (v InclusiveRange) Type() Type { + if v.InclusiveRangeType == nil { + // Return nil Type instead of Type referencing nil *InclusiveRangeType, + // so caller can check if v's type is nil and also prevent nil pointer dereference. + return nil + } + return v.InclusiveRangeType +} + +func (v InclusiveRange) MeteredType(common.MemoryGauge) Type { + return v.Type() +} + +func (v InclusiveRange) WithType(typ *InclusiveRangeType) InclusiveRange { + v.InclusiveRangeType = typ + return v +} + +func (v InclusiveRange) ToGoValue() any { + return []any{ + v.Start.ToGoValue(), + v.End.ToGoValue(), + v.Step.ToGoValue(), + } +} + +func (v InclusiveRange) String() string { + return formatComposite( + v.InclusiveRangeType.ID(), + []Field{}, // TODO: May be bring back the range formatter. + []Value{v.Start, v.End, v.Step}, + ) +} + // PathLink type PathLink struct { From 6fd4f2729d5feaaed0b4e0ee6dc762601d6ecf03 Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Tue, 25 Jul 2023 23:18:07 +0530 Subject: [PATCH 019/121] Add encoding/decoding/storage tests and fix bugs --- .../imported_values_memory_metering_test.go | 23 +++++ runtime/interpreter/encoding_test.go | 56 ++++++++++++ runtime/interpreter/statictype_test.go | 59 ++++++++++++ runtime/interpreter/value.go | 89 ++++++++++--------- runtime/interpreter/value_range.go | 2 +- runtime/sema/type.go | 14 ++- runtime/sema/type_tags.go | 34 +------ 7 files changed, 193 insertions(+), 84 deletions(-) diff --git a/runtime/imported_values_memory_metering_test.go b/runtime/imported_values_memory_metering_test.go index 822635d036..5587ce5237 100644 --- a/runtime/imported_values_memory_metering_test.go +++ b/runtime/imported_values_memory_metering_test.go @@ -398,6 +398,29 @@ func TestImportedValueMemoryMetering(t *testing.T) { assert.Equal(t, uint64(1), meter[common.MemoryKindCompositeValueBase]) assert.Equal(t, uint64(71), meter[common.MemoryKindRawString]) }) + + t.Run("InclusiveRange", func(t *testing.T) { + t.Parallel() + + script := []byte(` + pub fun main(x: InclusiveRange) {} + `) + + meter := make(map[common.MemoryKind]uint64) + inclusiveRangeValue := cadence.InclusiveRange{ + InclusiveRangeType: &cadence.InclusiveRangeType{ + ElementType: cadence.IntType{}, + }, + Start: cadence.NewInt(1), + End: cadence.NewInt(50), + Step: cadence.NewInt(2), + } + + executeScript(t, script, meter, inclusiveRangeValue) + assert.Equal(t, uint64(1), meter[common.MemoryKindCompositeValueBase]) + assert.Equal(t, uint64(1), meter[common.MemoryKindInclusiveRangeStaticType]) + assert.Equal(t, uint64(1), meter[common.MemoryKindCadenceInclusiveRangeValue]) + }) } type testMemoryError struct{} diff --git a/runtime/interpreter/encoding_test.go b/runtime/interpreter/encoding_test.go index 907dd8a123..5033fc8d96 100644 --- a/runtime/interpreter/encoding_test.go +++ b/runtime/interpreter/encoding_test.go @@ -3988,6 +3988,62 @@ func TestEncodeDecodePathLinkValue(t *testing.T) { ) }) + t.Run("inclusiverange, int", func(t *testing.T) { + + t.Parallel() + + value := PathLinkValue{ + TargetPath: publicPathValue, + Type: InclusiveRangeStaticType{ + ElementType: PrimitiveStaticTypeInt, + }, + } + + encoded := assemble( + // tag + 0xd8, CBORTagInclusiveRangeStaticType, + // tag + 0xd8, CBORTagPrimitiveStaticType, + // positive integer 36 + 0x18, 0x24, + ) + + testEncodeDecode(t, + encodeDecodeTest{ + value: value, + encoded: encoded, + }, + ) + }) + + t.Run("inclusiverange, uint256", func(t *testing.T) { + + t.Parallel() + + value := PathLinkValue{ + TargetPath: publicPathValue, + Type: InclusiveRangeStaticType{ + ElementType: PrimitiveStaticTypeUInt256, + }, + } + + encoded := assemble( + // tag + 0xd8, CBORTagInclusiveRangeStaticType, + // tag + 0xd8, CBORTagPrimitiveStaticType, + // positive integer 50 + 0x18, 0x32, + ) + + testEncodeDecode(t, + encodeDecodeTest{ + value: value, + encoded: encoded, + }, + ) + }) + t.Run("restricted", func(t *testing.T) { t.Parallel() diff --git a/runtime/interpreter/statictype_test.go b/runtime/interpreter/statictype_test.go index ddf2e5c15b..5a56175fc1 100644 --- a/runtime/interpreter/statictype_test.go +++ b/runtime/interpreter/statictype_test.go @@ -747,6 +747,56 @@ func TestDictionaryStaticType_Equal(t *testing.T) { }) } +func TestInclusiveRangeStaticType_Equal(t *testing.T) { + + t.Parallel() + + t.Run("equal", func(t *testing.T) { + + t.Parallel() + + require.True(t, + InclusiveRangeStaticType{ + ElementType: PrimitiveStaticTypeInt256, + }.Equal( + InclusiveRangeStaticType{ + ElementType: PrimitiveStaticTypeInt256, + }, + ), + ) + }) + + t.Run("different member types", func(t *testing.T) { + + t.Parallel() + + require.False(t, + InclusiveRangeStaticType{ + ElementType: PrimitiveStaticTypeInt, + }.Equal( + InclusiveRangeStaticType{ + ElementType: PrimitiveStaticTypeWord256, + }, + ), + ) + }) + + t.Run("different kind", func(t *testing.T) { + + t.Parallel() + + require.False(t, + InclusiveRangeStaticType{ + ElementType: PrimitiveStaticTypeInt, + }.Equal( + VariableSizedStaticType{ + Type: PrimitiveStaticTypeInt, + }, + ), + ) + }) +} + func TestRestrictedStaticType_Equal(t *testing.T) { t.Parallel() @@ -1375,6 +1425,15 @@ func TestStaticTypeConversion(t *testing.T) { ValueType: PrimitiveStaticTypeString, }, }, + { + name: "InclusiveRange", + semaType: &sema.InclusiveRangeType{ + MemberType: sema.IntType, + }, + staticType: InclusiveRangeStaticType{ + ElementType: PrimitiveStaticTypeInt, + }, + }, { name: "Restricted", semaType: &sema.RestrictedType{ diff --git a/runtime/interpreter/value.go b/runtime/interpreter/value.go index 20b63720b3..a7b4e1c8dc 100644 --- a/runtime/interpreter/value.go +++ b/runtime/interpreter/value.go @@ -17052,67 +17052,72 @@ func (v *CompositeValue) ConformsToStaticType( }() } - staticType := v.StaticType(interpreter).(CompositeStaticType) - + staticType := v.StaticType(interpreter) semaType := interpreter.MustConvertStaticToSemaType(staticType) - compositeType, ok := semaType.(*sema.CompositeType) - if !ok || - v.Kind != compositeType.Kind || - v.TypeID() != compositeType.ID() { + // CompositeValue is also used for storing types which aren't CompositeStaticType. + // E.g. InclusiveRange. + if _, ok := staticType.(CompositeStaticType); ok { + compositeType, ok := semaType.(*sema.CompositeType) + if !ok || + v.Kind != compositeType.Kind || + v.TypeID() != compositeType.ID() { - return false - } + return false + } + + if compositeType.Kind == common.CompositeKindAttachment { + base := v.getBaseValue().Value + if base == nil || !base.ConformsToStaticType(interpreter, locationRange, results) { + return false + } + } - if compositeType.Kind == common.CompositeKindAttachment { - base := v.getBaseValue().Value - if base == nil || !base.ConformsToStaticType(interpreter, locationRange, results) { + fieldsLen := int(v.dictionary.Count()) + if v.ComputedFields != nil { + fieldsLen += len(v.ComputedFields) + } + + if fieldsLen != len(compositeType.Fields) { return false } - } - fieldsLen := int(v.dictionary.Count()) - if v.ComputedFields != nil { - fieldsLen += len(v.ComputedFields) - } + for _, fieldName := range compositeType.Fields { + value := v.GetField(interpreter, locationRange, fieldName) + if value == nil { + if v.ComputedFields == nil { + return false + } - if fieldsLen != len(compositeType.Fields) { - return false - } + fieldGetter, ok := v.ComputedFields[fieldName] + if !ok { + return false + } - for _, fieldName := range compositeType.Fields { - value := v.GetField(interpreter, locationRange, fieldName) - if value == nil { - if v.ComputedFields == nil { - return false + value = fieldGetter(interpreter, locationRange) } - fieldGetter, ok := v.ComputedFields[fieldName] + member, ok := compositeType.Members.Get(fieldName) if !ok { return false } - value = fieldGetter(interpreter, locationRange) - } - - member, ok := compositeType.Members.Get(fieldName) - if !ok { - return false - } + fieldStaticType := value.StaticType(interpreter) - fieldStaticType := value.StaticType(interpreter) + if !interpreter.IsSubTypeOfSemaType(fieldStaticType, member.TypeAnnotation.Type) { + return false + } - if !interpreter.IsSubTypeOfSemaType(fieldStaticType, member.TypeAnnotation.Type) { - return false + if !value.ConformsToStaticType( + interpreter, + locationRange, + results, + ) { + return false + } } + } else { - if !value.ConformsToStaticType( - interpreter, - locationRange, - results, - ) { - return false - } } return true diff --git a/runtime/interpreter/value_range.go b/runtime/interpreter/value_range.go index b5b8ba7480..f34b27859d 100644 --- a/runtime/interpreter/value_range.go +++ b/runtime/interpreter/value_range.go @@ -43,7 +43,7 @@ func NewInclusiveRangeValue( step := interpreter.GetValueForIntegerType(1, rangeType.ElementType) if startComparable.Greater(interpreter, endComparable, locationRange) { elemSemaTy := interpreter.MustConvertStaticToSemaType(rangeType.ElementType) - if _, ok := sema.AllUnsignedIntegerTypesSet[elemSemaTy]; ok { + if elemSemaTy.Tag().BelongsTo(sema.UnsignedIntegerTypeTag) { panic(InclusiveRangeConstructionError{ LocationRange: locationRange, Message: fmt.Sprintf("step value cannot be negative for unsigned integer type %s", elemSemaTy), diff --git a/runtime/sema/type.go b/runtime/sema/type.go index 5f0d4596c8..7d62b7134d 100644 --- a/runtime/sema/type.go +++ b/runtime/sema/type.go @@ -3457,8 +3457,6 @@ var AllUnsignedIntegerTypes = []Type{ Word256Type, } -var AllUnsignedIntegerTypesSet = make(map[Type]struct{}) - var AllIntegerTypes = common.Concat( AllUnsignedIntegerTypes, AllSignedIntegerTypes, @@ -3606,11 +3604,6 @@ func init() { ) } } - - // Populate AllUnsignedIntegerTypesSet - for _, ty := range AllUnsignedIntegerTypes { - AllUnsignedIntegerTypesSet[ty] = struct{}{} - } } func NumberConversionFunctionType(numberType Type) *FunctionType { @@ -5230,7 +5223,7 @@ func (t *DictionaryType) Resolve(typeArguments *TypeParameterTypeOrderedMap) Typ } } -// InclusiveRangeType todo. +// InclusiveRangeType type InclusiveRangeType struct { MemberType Type @@ -5284,12 +5277,15 @@ func (r *InclusiveRangeType) Equal(other Type) bool { if !ok { return false } + if otherRange.MemberType == nil { + return r.MemberType == nil + } return otherRange.MemberType.Equal(r.MemberType) } func (r *InclusiveRangeType) IsResourceType() bool { - return r.MemberType.IsResourceType() + return false } func (r *InclusiveRangeType) IsInvalidType() bool { diff --git a/runtime/sema/type_tags.go b/runtime/sema/type_tags.go index 37f0ed2530..1c9a3d1766 100644 --- a/runtime/sema/type_tags.go +++ b/runtime/sema/type_tags.go @@ -674,12 +674,10 @@ func findSuperTypeFromUpperMask(joinedTypeTag TypeTag, types []Type) Type { restrictedTypeMask, transactionTypeMask, interfaceTypeMask, - functionTypeMask: + functionTypeMask, + inclusiveRangeTypeMask: return getSuperTypeOfDerivedTypes(types) - case inclusiveRangeTypeMask: - return commonSuperTypeOfRanges(types) - case anyResourceAttachmentMask: return AnyResourceAttachmentType @@ -697,34 +695,6 @@ func findSuperTypeFromUpperMask(joinedTypeTag TypeTag, types []Type) Type { } } -func commonSuperTypeOfRanges(types []Type) Type { - // We reach here if all types are range types. - // Therefore, decide the common supertype based on the member types. - - var memberTypes []Type - - for _, typ := range types { - // 'Never' type doesn't affect the supertype. - // Hence, ignore them - if typ == NeverType { - continue - } - - rangeType, ok := typ.(*InclusiveRangeType) - if !ok { - panic(errors.NewUnexpectedError("expected inclusive range type, found %s", typ)) - } - - memberTypes = append(memberTypes, rangeType.MemberType) - } - - memberSuperType := leastCommonSuperType(memberTypes...) - - return &InclusiveRangeType{ - MemberType: memberSuperType, - } -} - func getSuperTypeOfDerivedTypes(types []Type) Type { // We reach here if all types belongs to same kind. // e.g: All are arrays, all are dictionaries, etc. From 5386f43865ae22801aa912252d4e654a907799b8 Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Tue, 25 Jul 2023 23:45:02 +0530 Subject: [PATCH 020/121] Add conformance checks for CompositeValue which are not CompositeType --- runtime/interpreter/value.go | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/runtime/interpreter/value.go b/runtime/interpreter/value.go index a7b4e1c8dc..1be2f3f23d 100644 --- a/runtime/interpreter/value.go +++ b/runtime/interpreter/value.go @@ -17116,8 +17116,31 @@ func (v *CompositeValue) ConformsToStaticType( return false } } - } else { + } else if _, ok := staticType.(InclusiveRangeStaticType); ok { + inclusiveRangeType, ok := semaType.(*sema.InclusiveRangeType) + if !ok { + return false + } + + startValue := v.GetField(interpreter, locationRange, sema.InclusiveRangeTypeStartFieldName) + endValue := v.GetField(interpreter, locationRange, sema.InclusiveRangeTypeEndFieldName) + stepValue := v.GetField(interpreter, locationRange, sema.InclusiveRangeTypeStepFieldName) + + for _, value := range []Value{startValue, endValue, stepValue} { + fieldStaticType := value.StaticType(interpreter) + + if !interpreter.IsSubTypeOfSemaType(fieldStaticType, inclusiveRangeType.MemberType) { + return false + } + if !value.ConformsToStaticType( + interpreter, + locationRange, + results, + ) { + return false + } + } } return true From d72ddf56f4a5ed1b29f7eab7d545c3a1103452df Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Thu, 27 Jul 2023 20:02:33 +0530 Subject: [PATCH 021/121] Add more tests - values, dynamic casting, ccf, json --- encoding/ccf/ccf_test.go | 63 ++++++++++ encoding/json/decode.go | 6 + encoding/json/encoding_test.go | 63 ++++++++++ .../tests/interpreter/dynamic_casting_test.go | 119 ++++++++++++++++++ values.go | 24 +++- values_test.go | 8 ++ 6 files changed, 282 insertions(+), 1 deletion(-) diff --git a/encoding/ccf/ccf_test.go b/encoding/ccf/ccf_test.go index 29c145678d..3d2ccb13b4 100644 --- a/encoding/ccf/ccf_test.go +++ b/encoding/ccf/ccf_test.go @@ -5700,6 +5700,69 @@ func TestEncodeStruct(t *testing.T) { ) } +func TestEncodeInclusiveRange(t *testing.T) { + + t.Parallel() + + simpleInclusiveRange := encodeTest{ + name: "simpleInclusiveRange", + val: func() cadence.Value { + return cadence.NewInclusiveRange( + cadence.NewInt256(10), + cadence.NewInt256(20), + cadence.NewInt256(5), + ).WithType(cadence.NewInclusiveRangeType(cadence.NewInt256Type())) + }(), + expected: []byte{ + // language=json, format=json-cdc + // {"type":"InclusiveRange","value":[{"type":"Int256","value":"10"},{"type":"Int256","value":"20"},{"type":"Int256","value":"5"}]} + // + // language=edn, format=ccf + // 130([145([137(10)]), [10, 20, 5]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // type (InclusiveRange) + // tag + 0xd8, ccf.CBORTagInclusiveRangeType, + // array, 1 item follows + 0x81, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int256 type ID (10) + 0x0a, + // array data without inlined type definition + // array, 3 items follow + 0x83, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 10 + 0xa, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 20 + 0x14, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 5 + 0x5, + }, + } + + testAllEncodeAndDecode(t, + simpleInclusiveRange, + ) +} + func TestEncodeEvent(t *testing.T) { t.Parallel() diff --git a/encoding/json/decode.go b/encoding/json/decode.go index 30dde19be8..3a9abca48c 100644 --- a/encoding/json/decode.go +++ b/encoding/json/decode.go @@ -136,6 +136,7 @@ const ( returnKey = "return" typeBoundKey = "typeBound" functionTypeKey = "functionType" + elementKey = "element" startKey = "start" endKey = "end" stepKey = "step" @@ -1211,6 +1212,11 @@ func (d *Decoder) decodeType(valueJSON any, results typeDecodingResults) cadence d.decodeType(obj.Get(keyKey), results), d.decodeType(obj.Get(valueKey), results), ) + case "InclusiveRange": + return cadence.NewMeteredInclusiveRangeType( + d.gauge, + d.decodeType(obj.Get(elementKey), results), + ) case "ConstantSizedArray": size := toUInt(obj.Get(sizeKey)) return cadence.NewMeteredConstantSizedArrayType( diff --git a/encoding/json/encoding_test.go b/encoding/json/encoding_test.go index 65afb32b94..f00e6f71bf 100644 --- a/encoding/json/encoding_test.go +++ b/encoding/json/encoding_test.go @@ -1448,6 +1448,42 @@ func TestEncodeStruct(t *testing.T) { testAllEncodeAndDecode(t, simpleStruct, resourceStruct) } +func TestEncodeInclusiveRange(t *testing.T) { + + t.Parallel() + + simpleInclusiveRange := encodeTest{ + "Simple", + cadence.NewInclusiveRange( + cadence.NewInt256(10), + cadence.NewInt256(20), + cadence.NewInt256(5), + ), + // language=json + ` + { + "type": "InclusiveRange", + "value": { + "start": { + "type": "Int256", + "value": "10" + }, + "end": { + "type": "Int256", + "value": "20" + }, + "step": { + "type": "Int256", + "value": "5" + } + } + } + `, + } + + testAllEncodeAndDecode(t, simpleInclusiveRange) +} + func TestEncodeEvent(t *testing.T) { t.Parallel() @@ -1932,6 +1968,33 @@ func TestEncodeType(t *testing.T) { }) + t.Run("with static InclusiveRange", func(t *testing.T) { + + testEncodeAndDecode( + t, + cadence.TypeValue{ + StaticType: &cadence.InclusiveRangeType{ + ElementType: cadence.IntType{}, + }, + }, + // language=json + ` + { + "type": "Type", + "value": { + "staticType": { + "kind": "InclusiveRange", + "element": { + "kind": "Int" + } + } + } + } + `, + ) + + }) + t.Run("with static struct", func(t *testing.T) { testEncodeAndDecode( diff --git a/runtime/tests/interpreter/dynamic_casting_test.go b/runtime/tests/interpreter/dynamic_casting_test.go index ae820bfaf3..f3d0c980ec 100644 --- a/runtime/tests/interpreter/dynamic_casting_test.go +++ b/runtime/tests/interpreter/dynamic_casting_test.go @@ -1467,6 +1467,125 @@ func TestInterpretDynamicCastingDictionary(t *testing.T) { } } +func TestInterpretDynamicCastingInclusiveRange(t *testing.T) { + + t.Parallel() + + types := []sema.Type{ + &sema.InclusiveRangeType{ + MemberType: sema.IntType, + }, + &sema.InclusiveRangeType{ + MemberType: sema.IntegerType, + }, + } + + baseValueActivation := sema.NewVariableActivation(sema.BaseValueActivation) + baseValueActivation.DeclareValue(stdlib.InclusiveRangeConstructorFunction) + + baseActivation := activations.NewActivation(nil, interpreter.BaseActivation) + interpreter.Declare(baseActivation, stdlib.InclusiveRangeConstructorFunction) + + options := ParseCheckAndInterpretOptions{ + CheckerConfig: &sema.Config{ + BaseValueActivation: baseValueActivation, + }, + Config: &interpreter.Config{ + BaseActivation: baseActivation, + }, + } + + for operation, returnsOptional := range dynamicCastingOperations { + + t.Run(operation.Symbol(), func(t *testing.T) { + + for _, fromType := range types { + for _, targetType := range types { + + t.Run(fmt.Sprintf("valid: from %s to %s", fromType, targetType), func(t *testing.T) { + + inter, err := parseCheckAndInterpretWithOptions(t, + fmt.Sprintf( + ` + let x: InclusiveRange = InclusiveRange(10, 20) + let y: %[1]s = x + let z: %[2]s? = y %[3]s %[2]s + `, + fromType, + targetType, + operation.Symbol(), + ), + options, + ) + require.NoError(t, err) + + expectedInclusiveRange := interpreter.NewInclusiveRangeValue( + inter, + interpreter.EmptyLocationRange, + interpreter.NewUnmeteredIntValueFromInt64(10), + interpreter.NewUnmeteredIntValueFromInt64(20), + interpreter.InclusiveRangeStaticType{ + ElementType: interpreter.PrimitiveStaticTypeInt, + }, + ) + + AssertValuesEqual( + t, + inter, + expectedInclusiveRange, + inter.Globals.Get("y").GetValue(), + ) + + AssertValuesEqual( + t, + inter, + interpreter.NewUnmeteredSomeValueNonCopying( + expectedInclusiveRange, + ), + inter.Globals.Get("z").GetValue(), + ) + }) + } + + // We cannot test for invalid casts for T since InclusiveRange has a type bound Integer on T. + } + + t.Run("invalid upcast", func(t *testing.T) { + + inter, err := parseCheckAndInterpretWithOptions(t, + fmt.Sprintf( + ` + fun test(): InclusiveRange? { + let x: InclusiveRange = InclusiveRange(10, 20) + return x %s InclusiveRange + } + `, + operation.Symbol(), + ), + options, + ) + require.NoError(t, err) + + result, err := inter.Invoke("test") + + if returnsOptional { + require.NoError(t, err) + AssertValuesEqual( + t, + inter, + interpreter.Nil, + result, + ) + } else { + RequireError(t, err) + + require.ErrorAs(t, err, &interpreter.ForceCastTypeMismatchError{}) + } + }) + }) + } +} + func TestInterpretDynamicCastingResourceType(t *testing.T) { t.Parallel() diff --git a/values.go b/values.go index bda8a7e431..2995347fb9 100644 --- a/values.go +++ b/values.go @@ -2058,6 +2058,7 @@ type InclusiveRange struct { Start Value End Value Step Value + fields []Field } var _ Value = InclusiveRange{} @@ -2107,9 +2108,30 @@ func (v InclusiveRange) ToGoValue() any { } func (v InclusiveRange) String() string { + if v.InclusiveRangeType == nil { + return "" + } + + if v.fields == nil { + v.fields = []Field{ + { + Identifier: sema.InclusiveRangeTypeStartFieldName, + Type: v.InclusiveRangeType.ElementType, + }, + { + Identifier: sema.InclusiveRangeTypeEndFieldName, + Type: v.InclusiveRangeType.ElementType, + }, + { + Identifier: sema.InclusiveRangeTypeStepFieldName, + Type: v.InclusiveRangeType.ElementType, + }, + } + } + return formatComposite( v.InclusiveRangeType.ID(), - []Field{}, // TODO: May be bring back the range formatter. + v.fields, []Value{v.Start, v.End, v.Step}, ) } diff --git a/values_test.go b/values_test.go index ff9d4937b5..65bd6eb97c 100644 --- a/values_test.go +++ b/values_test.go @@ -220,6 +220,14 @@ func newValueTestCases() map[string]valueTestCase { }, string: "{\"key\": \"value\"}", }, + "InclusiveRange": { + value: NewInclusiveRange(NewInt(85), NewInt(-85), NewInt(-2)), + exampleType: NewInclusiveRangeType(IntType{}), + withType: func(value Value, ty Type) Value { + return value.(InclusiveRange).WithType(ty.(*InclusiveRangeType)) + }, + string: "InclusiveRange(start: 85, end: -85, step: -2)", + }, "Bytes": { value: NewBytes([]byte{0x1, 0x2}), string: "[0x1, 0x2]", From ae2917945c9a96c350b9d9250d987c7ffcaf2d21 Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Thu, 27 Jul 2023 22:29:16 +0530 Subject: [PATCH 022/121] Make cachedIntegerValues a global value --- runtime/interpreter/interpreter.go | 46 ++++++++++++++++-------------- runtime/interpreter/value_range.go | 6 ++-- 2 files changed, 27 insertions(+), 25 deletions(-) diff --git a/runtime/interpreter/interpreter.go b/runtime/interpreter/interpreter.go index 5cd38a3157..7b162188ee 100644 --- a/runtime/interpreter/interpreter.go +++ b/runtime/interpreter/interpreter.go @@ -242,15 +242,14 @@ type Storage interface { type ReferencedResourceKindedValues map[atree.StorageID]map[ReferenceTrackedResourceKindedValue]struct{} type Interpreter struct { - Location common.Location - statement ast.Statement - Program *Program - SharedState *SharedState - Globals GlobalVariables - activations *VariableActivations - Transactions []*HostFunctionValue - interpreted bool - cachedIntegerValues map[StaticType]map[int64]IntegerValue + Location common.Location + statement ast.Statement + Program *Program + SharedState *SharedState + Globals GlobalVariables + activations *VariableActivations + Transactions []*HostFunctionValue + interpreted bool } var _ common.MemoryGauge = &Interpreter{} @@ -262,10 +261,14 @@ var _ ast.ExpressionVisitor[Value] = &Interpreter{} // It is reused across all interpreters. var BaseActivation *VariableActivation +var cachedIntegerValues map[StaticType]map[int8]IntegerValue + func init() { // No need to meter since this is only created once BaseActivation = activations.NewActivation[*Variable](nil, nil) defineBaseFunctions(BaseActivation) + + cachedIntegerValues = make(map[StaticType]map[int8]IntegerValue) } func NewInterpreter( @@ -287,10 +290,9 @@ func NewInterpreterWithSharedState( ) (*Interpreter, error) { interpreter := &Interpreter{ - Program: program, - Location: location, - SharedState: sharedState, - cachedIntegerValues: make(map[StaticType]map[int64]IntegerValue), + Program: program, + Location: location, + SharedState: sharedState, } // Register self @@ -2357,8 +2359,8 @@ func (interpreter *Interpreter) WriteStored( // Get the provided int64 value in the required staticType. // Note: Assumes that the provided value fits within the constraints of the staticType. -func (interpreter *Interpreter) GetValueForIntegerType(value int64, staticType StaticType) IntegerValue { - typedCache, typedOk := interpreter.cachedIntegerValues[staticType] +func GetValueForIntegerType(value int8, staticType StaticType) IntegerValue { + typedCache, typedOk := cachedIntegerValues[staticType] if typedOk { val, ok := typedCache[value] if ok { @@ -2368,16 +2370,16 @@ func (interpreter *Interpreter) GetValueForIntegerType(value int64, staticType S val := getValueForIntegerType(value, staticType) if !typedOk { - interpreter.cachedIntegerValues[staticType] = make(map[int64]IntegerValue) + cachedIntegerValues[staticType] = make(map[int8]IntegerValue) } - interpreter.cachedIntegerValues[staticType][value] = val + cachedIntegerValues[staticType][value] = val return val } -func getValueForIntegerType(value int64, staticType StaticType) IntegerValue { +func getValueForIntegerType(value int8, staticType StaticType) IntegerValue { switch staticType { case PrimitiveStaticTypeInt: - return NewUnmeteredIntValueFromInt64(value) + return NewUnmeteredIntValueFromInt64(int64(value)) case PrimitiveStaticTypeInt8: return NewUnmeteredInt8Value(int8(value)) case PrimitiveStaticTypeInt16: @@ -2385,11 +2387,11 @@ func getValueForIntegerType(value int64, staticType StaticType) IntegerValue { case PrimitiveStaticTypeInt32: return NewUnmeteredInt32Value(int32(value)) case PrimitiveStaticTypeInt64: - return NewUnmeteredInt64Value(value) + return NewUnmeteredInt64Value(int64(value)) case PrimitiveStaticTypeInt128: - return NewUnmeteredInt128ValueFromInt64(value) + return NewUnmeteredInt128ValueFromInt64(int64(value)) case PrimitiveStaticTypeInt256: - return NewUnmeteredInt256ValueFromInt64(value) + return NewUnmeteredInt256ValueFromInt64(int64(value)) case PrimitiveStaticTypeUInt: return NewUnmeteredUIntValueFromUint64(uint64(value)) diff --git a/runtime/interpreter/value_range.go b/runtime/interpreter/value_range.go index f34b27859d..cff681e0aa 100644 --- a/runtime/interpreter/value_range.go +++ b/runtime/interpreter/value_range.go @@ -40,7 +40,7 @@ func NewInclusiveRangeValue( panic(errors.NewUnreachableError()) } - step := interpreter.GetValueForIntegerType(1, rangeType.ElementType) + step := GetValueForIntegerType(1, rangeType.ElementType) if startComparable.Greater(interpreter, endComparable, locationRange) { elemSemaTy := interpreter.MustConvertStaticToSemaType(rangeType.ElementType) if elemSemaTy.Tag().BelongsTo(sema.UnsignedIntegerTypeTag) { @@ -71,7 +71,7 @@ func NewInclusiveRangeValueWithStep( rangeType InclusiveRangeStaticType, ) *CompositeValue { - zeroValue := interpreter.GetValueForIntegerType(0, rangeType.ElementType) + zeroValue := GetValueForIntegerType(0, rangeType.ElementType) // Validate that the step is non-zero. if step.Equal(interpreter, locationRange, zeroValue) { @@ -186,7 +186,7 @@ func rangeContains( panic(errors.NewUnreachableError()) } - zeroValue := interpreter.GetValueForIntegerType(0, rangeType.ElementType) + zeroValue := GetValueForIntegerType(0, rangeType.ElementType) mod := diff.Mod(interpreter, step, locationRange) result = mod.Equal(interpreter, locationRange, zeroValue) } From efb0b745d2edf2e92455b06a496fb87fde3c035c Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Thu, 27 Jul 2023 22:46:01 +0530 Subject: [PATCH 023/121] Ensure that inner type is an integer in runtime type constructor --- runtime/interpreter/interpreter.go | 10 +++++----- runtime/tests/interpreter/range_value_test.go | 12 ++++++------ 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/runtime/interpreter/interpreter.go b/runtime/interpreter/interpreter.go index 7b162188ee..f74229b8e5 100644 --- a/runtime/interpreter/interpreter.go +++ b/runtime/interpreter/interpreter.go @@ -2381,7 +2381,7 @@ func getValueForIntegerType(value int8, staticType StaticType) IntegerValue { case PrimitiveStaticTypeInt: return NewUnmeteredIntValueFromInt64(int64(value)) case PrimitiveStaticTypeInt8: - return NewUnmeteredInt8Value(int8(value)) + return NewUnmeteredInt8Value(value) case PrimitiveStaticTypeInt16: return NewUnmeteredInt16Value(int16(value)) case PrimitiveStaticTypeInt32: @@ -3636,10 +3636,10 @@ var runtimeTypeConstructors = []runtimeTypeConstructor{ ty := typeValue.Type // InclusiveRanges must hold integers - // _, ok = ty.(Sig) - // if !ok { - // return Nil - // } + elemSemaTy := invocation.Interpreter.MustConvertStaticToSemaType(ty) + if !elemSemaTy.Tag().BelongsTo(sema.IntegerTypeTag) { + return Nil + } return NewSomeValueNonCopying( invocation.Interpreter, diff --git a/runtime/tests/interpreter/range_value_test.go b/runtime/tests/interpreter/range_value_test.go index 41e8fabbbd..dde6acecf1 100644 --- a/runtime/tests/interpreter/range_value_test.go +++ b/runtime/tests/interpreter/range_value_test.go @@ -41,7 +41,7 @@ type containsTestCase struct { type inclusiveRangeConstructionTest struct { ty sema.Type - s, e, step int64 + s, e, step int8 containsTests []containsTestCase } @@ -416,17 +416,17 @@ func TestInclusiveRange(t *testing.T) { expectedRangeValue = interpreter.NewInclusiveRangeValueWithStep( inter, interpreter.EmptyLocationRange, - inter.GetValueForIntegerType(testCase.s, elementType), - inter.GetValueForIntegerType(testCase.e, elementType), - inter.GetValueForIntegerType(testCase.step, elementType), + interpreter.GetValueForIntegerType(testCase.s, elementType), + interpreter.GetValueForIntegerType(testCase.e, elementType), + interpreter.GetValueForIntegerType(testCase.step, elementType), rangeType, ) } else { expectedRangeValue = interpreter.NewInclusiveRangeValue( inter, interpreter.EmptyLocationRange, - inter.GetValueForIntegerType(testCase.s, elementType), - inter.GetValueForIntegerType(testCase.e, elementType), + interpreter.GetValueForIntegerType(testCase.s, elementType), + interpreter.GetValueForIntegerType(testCase.e, elementType), rangeType, ) } From 96fe5581644c1129852942122d40823da508e15d Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Thu, 27 Jul 2023 22:50:13 +0530 Subject: [PATCH 024/121] Remove dead code for super type of InclusiveRangeType --- runtime/sema/type.go | 8 -------- 1 file changed, 8 deletions(-) diff --git a/runtime/sema/type.go b/runtime/sema/type.go index 7d62b7134d..23df60db96 100644 --- a/runtime/sema/type.go +++ b/runtime/sema/type.go @@ -6017,14 +6017,6 @@ func checkSubTypeWithoutEquality(subType Type, superType Type) bool { return IsSubType(typedSubType.KeyType, typedSuperType.KeyType) && IsSubType(typedSubType.ValueType, typedSuperType.ValueType) - // case *InclusiveRangeType: - // typedSubType, ok := subType.(*InclusiveRangeType) - // if !ok { - // return false - // } - - // return IsSubType(typedSubType.MemberType, typedSuperType.MemberType) - case *VariableSizedType: typedSubType, ok := subType.(*VariableSizedType) if !ok { From 4dac857e8a758df8c8abb06e1ed4932ab08e8c40 Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Thu, 27 Jul 2023 23:15:23 +0530 Subject: [PATCH 025/121] Take Sema type in param instead of calculating in constructor --- runtime/convertValues.go | 1 + runtime/convertValues_test.go | 1 + runtime/interpreter/storage_test.go | 1 + runtime/interpreter/value_range.go | 16 ++++++---------- runtime/stdlib/range.go | 11 ++++++++++- .../tests/interpreter/dynamic_casting_test.go | 1 + runtime/tests/interpreter/range_value_test.go | 3 +++ 7 files changed, 23 insertions(+), 11 deletions(-) diff --git a/runtime/convertValues.go b/runtime/convertValues.go index 82b7dd09c3..12b8f1cc9f 100644 --- a/runtime/convertValues.go +++ b/runtime/convertValues.go @@ -1511,6 +1511,7 @@ func (i valueImporter) importInclusiveRangeValue( endInteger, stepInteger, inclusiveRangeStaticType, + inclusiveRangeType, ), nil } diff --git a/runtime/convertValues_test.go b/runtime/convertValues_test.go index 3f5fa78378..19defe7350 100644 --- a/runtime/convertValues_test.go +++ b/runtime/convertValues_test.go @@ -1521,6 +1521,7 @@ func TestImportInclusiveRangeValue(t *testing.T) { interpreter.InclusiveRangeStaticType{ ElementType: interpreter.PrimitiveStaticTypeInt, }, + sema.NewInclusiveRangeType(nil, sema.IntType), ), actual, ) diff --git a/runtime/interpreter/storage_test.go b/runtime/interpreter/storage_test.go index 28800e539e..23a0d73b63 100644 --- a/runtime/interpreter/storage_test.go +++ b/runtime/interpreter/storage_test.go @@ -106,6 +106,7 @@ func TestInclusiveRangeStorage(t *testing.T) { NewUnmeteredInt16Value(100), NewUnmeteredInt16Value(5), NewInclusiveRangeStaticType(inter, PrimitiveStaticTypeInt16), + sema.NewInclusiveRangeType(inter, sema.Int16Type), ) require.NotEqual(t, atree.StorageIDUndefined, value.StorageID()) diff --git a/runtime/interpreter/value_range.go b/runtime/interpreter/value_range.go index cff681e0aa..228b6ffb12 100644 --- a/runtime/interpreter/value_range.go +++ b/runtime/interpreter/value_range.go @@ -32,7 +32,8 @@ func NewInclusiveRangeValue( locationRange LocationRange, start IntegerValue, end IntegerValue, - rangeType InclusiveRangeStaticType, + rangeStaticType InclusiveRangeStaticType, + rangeSemaType *sema.InclusiveRangeType, ) *CompositeValue { startComparable, startOk := start.(ComparableValue) endComparable, endOk := end.(ComparableValue) @@ -40,9 +41,9 @@ func NewInclusiveRangeValue( panic(errors.NewUnreachableError()) } - step := GetValueForIntegerType(1, rangeType.ElementType) + step := GetValueForIntegerType(1, rangeStaticType.ElementType) if startComparable.Greater(interpreter, endComparable, locationRange) { - elemSemaTy := interpreter.MustConvertStaticToSemaType(rangeType.ElementType) + elemSemaTy := interpreter.MustConvertStaticToSemaType(rangeStaticType.ElementType) if elemSemaTy.Tag().BelongsTo(sema.UnsignedIntegerTypeTag) { panic(InclusiveRangeConstructionError{ LocationRange: locationRange, @@ -58,7 +59,7 @@ func NewInclusiveRangeValue( step = negatedStep } - return NewInclusiveRangeValueWithStep(interpreter, locationRange, start, end, step, rangeType) + return NewInclusiveRangeValueWithStep(interpreter, locationRange, start, end, step, rangeStaticType, rangeSemaType) } // NewInclusiveRangeValue constructs an InclusiveRange value with the provided start, end & step. @@ -69,6 +70,7 @@ func NewInclusiveRangeValueWithStep( end IntegerValue, step IntegerValue, rangeType InclusiveRangeStaticType, + rangeSemaType *sema.InclusiveRangeType, ) *CompositeValue { zeroValue := GetValueForIntegerType(0, rangeType.ElementType) @@ -114,8 +116,6 @@ func NewInclusiveRangeValueWithStep( }, } - rangeSemaType := getInclusiveRangeSemaType(interpreter, rangeType) - rangeValue := NewCompositeValueWithStaticType( interpreter, locationRange, @@ -150,10 +150,6 @@ func NewInclusiveRangeValueWithStep( return rangeValue } -func getInclusiveRangeSemaType(interpreter *Interpreter, rangeType InclusiveRangeStaticType) *sema.InclusiveRangeType { - return interpreter.MustConvertStaticToSemaType(rangeType).(*sema.InclusiveRangeType) -} - func rangeContains( rangeValue *CompositeValue, rangeType InclusiveRangeStaticType, diff --git a/runtime/stdlib/range.go b/runtime/stdlib/range.go index b47824d07a..191b5909fc 100644 --- a/runtime/stdlib/range.go +++ b/runtime/stdlib/range.go @@ -103,6 +103,7 @@ var InclusiveRangeConstructorFunction = NewStandardLibraryFunction( } rangeStaticType := interpreter.InclusiveRangeStaticType{ElementType: startStaticType} + rangeSemaType := sema.NewInclusiveRangeType(invocation.Interpreter, invocation.ArgumentTypes[0]) if len(invocation.Arguments) > 2 { step, ok := invocation.Arguments[2].(interpreter.IntegerValue) @@ -127,9 +128,17 @@ var InclusiveRangeConstructorFunction = NewStandardLibraryFunction( end, step, rangeStaticType, + rangeSemaType, ) } else { - return interpreter.NewInclusiveRangeValue(inter, locationRange, start, end, rangeStaticType) + return interpreter.NewInclusiveRangeValue( + inter, + locationRange, + start, + end, + rangeStaticType, + rangeSemaType, + ) } }, ) diff --git a/runtime/tests/interpreter/dynamic_casting_test.go b/runtime/tests/interpreter/dynamic_casting_test.go index f3d0c980ec..71cd10af7e 100644 --- a/runtime/tests/interpreter/dynamic_casting_test.go +++ b/runtime/tests/interpreter/dynamic_casting_test.go @@ -1527,6 +1527,7 @@ func TestInterpretDynamicCastingInclusiveRange(t *testing.T) { interpreter.InclusiveRangeStaticType{ ElementType: interpreter.PrimitiveStaticTypeInt, }, + sema.NewInclusiveRangeType(nil, sema.IntType), ) AssertValuesEqual( diff --git a/runtime/tests/interpreter/range_value_test.go b/runtime/tests/interpreter/range_value_test.go index dde6acecf1..d18f26764e 100644 --- a/runtime/tests/interpreter/range_value_test.go +++ b/runtime/tests/interpreter/range_value_test.go @@ -409,6 +409,7 @@ func TestInclusiveRange(t *testing.T) { testCase.ty, ) rangeType := interpreter.NewInclusiveRangeStaticType(nil, elementType) + rangeSemaType := sema.NewInclusiveRangeType(nil, testCase.ty) var expectedRangeValue *interpreter.CompositeValue @@ -420,6 +421,7 @@ func TestInclusiveRange(t *testing.T) { interpreter.GetValueForIntegerType(testCase.e, elementType), interpreter.GetValueForIntegerType(testCase.step, elementType), rangeType, + rangeSemaType, ) } else { expectedRangeValue = interpreter.NewInclusiveRangeValue( @@ -428,6 +430,7 @@ func TestInclusiveRange(t *testing.T) { interpreter.GetValueForIntegerType(testCase.s, elementType), interpreter.GetValueForIntegerType(testCase.e, elementType), rangeType, + rangeSemaType, ) } From aa36e254809279b5015533772904bdd608ad4f29 Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Thu, 27 Jul 2023 23:20:15 +0530 Subject: [PATCH 026/121] Split out construction of InclusiveRange into a third-private function --- runtime/interpreter/value_range.go | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/runtime/interpreter/value_range.go b/runtime/interpreter/value_range.go index 228b6ffb12..a70bb3da61 100644 --- a/runtime/interpreter/value_range.go +++ b/runtime/interpreter/value_range.go @@ -59,7 +59,7 @@ func NewInclusiveRangeValue( step = negatedStep } - return NewInclusiveRangeValueWithStep(interpreter, locationRange, start, end, step, rangeStaticType, rangeSemaType) + return createInclusiveRange(interpreter, locationRange, start, end, step, rangeStaticType, rangeSemaType) } // NewInclusiveRangeValue constructs an InclusiveRange value with the provided start, end & step. @@ -101,6 +101,18 @@ func NewInclusiveRangeValueWithStep( }) } + return createInclusiveRange(interpreter, locationRange, start, end, step, rangeType, rangeSemaType) +} + +func createInclusiveRange( + interpreter *Interpreter, + locationRange LocationRange, + start IntegerValue, + end IntegerValue, + step IntegerValue, + rangeType InclusiveRangeStaticType, + rangeSemaType *sema.InclusiveRangeType, +) *CompositeValue { fields := []CompositeField{ { Name: sema.InclusiveRangeTypeStartFieldName, From 6b03f038b92efa3116ff2e64a3834b2670ea6fa8 Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Sun, 6 Aug 2023 00:46:16 +0530 Subject: [PATCH 027/121] Add more tests case for ccf encoding/decoding --- encoding/ccf/ccf_test.go | 330 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 330 insertions(+) diff --git a/encoding/ccf/ccf_test.go b/encoding/ccf/ccf_test.go index 3d2ccb13b4..b550f32f01 100644 --- a/encoding/ccf/ccf_test.go +++ b/encoding/ccf/ccf_test.go @@ -5758,8 +5758,51 @@ func TestEncodeInclusiveRange(t *testing.T) { }, } + inclusiveRangeWithPrimitive := encodeTest{ + name: "simpleInclusiveRange", + val: func() cadence.Value { + return cadence.NewInclusiveRange( + cadence.NewInt8(10), + cadence.NewInt8(20), + cadence.NewInt8(5), + ).WithType(cadence.NewInclusiveRangeType(cadence.NewInt8Type())) + }(), + expected: []byte{ + // language=json, format=json-cdc + // {"type":"InclusiveRange","value":[{"type":"Int8","value":"10"},{"type":"Int8","value":"20"},{"type":"Int8","value":"5"}]} + // + // language=edn, format=ccf + // 130([145([137(5)]), [10, 20, 5]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // type (InclusiveRange) + // tag + 0xd8, ccf.CBORTagInclusiveRangeType, + // array, 1 item follows + 0x81, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int8 type ID (5) + 0x05, + // array data without inlined type definition + // array, 3 items follow + 0x83, + // element 1, 10 + 0xa, + // element 2, 20 + 0x14, + // element 3, 5 + 0x5, + }, + } + testAllEncodeAndDecode(t, simpleInclusiveRange, + inclusiveRangeWithPrimitive, ) } @@ -8148,6 +8191,45 @@ func TestEncodeType(t *testing.T) { }) + t.Run("with static InclusiveRange", func(t *testing.T) { + t.Parallel() + + testEncodeAndDecode( + t, + cadence.TypeValue{ + StaticType: &cadence.InclusiveRangeType{ + ElementType: cadence.IntType{}, + }, + }, + []byte{ + // language=json, format=json-cdc + // {"type":"Type","value":{"staticType":{"kind":"InclusiveRange", "element" : {"kind" : "Int"}}}} + // + // language=edn, format=ccf + // 130([137(41), 194([185(4)])]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagInclusiveRangeTypeValue, + // array, 1 element follows + 0x81, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // Int type (4) + 0x04, + }, + ) + + }) + t.Run("with static struct with no field", func(t *testing.T) { t.Parallel() @@ -14060,6 +14142,231 @@ func TestDecodeInvalidData(t *testing.T) { 0x01, }, }, + { + name: "nil element type in inclusiverange type", + data: []byte{ + // language=edn, format=ccf + // 130([145([nil]), [10, 20, 5]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // type (InclusiveRange) + // tag + 0xd8, ccf.CBORTagInclusiveRangeType, + // array, 1 item follow + 0x81, + // null + 0xf6, + // array data without inlined type definition + // array, 3 items follow + 0x83, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 10 + 0xa, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 20 + 0x14, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 5 + 0x5, + }, + }, + { + name: "invalid array head in inclusiverange value", + data: []byte{ + // language=edn, format=ccf + // 130([145([4]), [10, 20, 5]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // type (InclusiveRange) + // tag + 0xd8, ccf.CBORTagInclusiveRangeType, + // array, 1 item follow + 0x81, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // primitive type where array was expected + 0xe0, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 10 + 0xa, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 20 + 0x14, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 5 + 0x5, + }, + }, + { + name: "incorrect member count (2 instead of 3) in inclusiverange value", + data: []byte{ + // language=edn, format=ccf + // 130([145([4]), [10, 20, 5]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // type (InclusiveRange) + // tag + 0xd8, ccf.CBORTagInclusiveRangeType, + // array, 1 item follow + 0x81, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // array data without inlined type definition + // array, 2 items follow where 3 were expected + 0x82, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 10 + 0xa, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 20 + 0x14, + }, + }, + { + name: "invalid start value in inclusiverange value", + data: []byte{ + // language=edn, format=ccf + // 130([145([5]), [10, 20, 5]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // type (InclusiveRange) + // tag + 0xd8, ccf.CBORTagInclusiveRangeType, + // array, 1 item follow + 0x81, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int8 type ID (5) + 0x05, + // array data without inlined type definition + // array, 3 items follow + 0x83, + // tag (big num) but expected an Int8 + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 10 + 0xa, + // 20 + 0x14, + // 5 + 0x05, + }, + }, + { + name: "invalid end value in inclusiverange value", + data: []byte{ + // language=edn, format=ccf + // 130([145([5]), [10, 20, 5]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // type (InclusiveRange) + // tag + 0xd8, ccf.CBORTagInclusiveRangeType, + // array, 1 item follow + 0x81, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int8 type ID (5) + 0x05, + // array data without inlined type definition + // array, 3 items follow + 0x83, + // 10 + 0xa, + // tag (big num) but expected an Int8 + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 20 + 0x14, + // 5 + 0x05, + }, + }, + { + name: "invalid step value in inclusiverange value", + data: []byte{ + // language=edn, format=ccf + // 130([145([5]), [10, 20, 5]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // type (InclusiveRange) + // tag + 0xd8, ccf.CBORTagInclusiveRangeType, + // array, 1 item follow + 0x81, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int8 type ID (5) + 0x05, + // array data without inlined type definition + // array, 3 items follow + 0x83, + // 10 + 0xa, + // 20 + 0x14, + // tag (big num) but expected an Int8 + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 5 + 0x05, + }, + }, { name: "nil composite field type", data: []byte{ @@ -14256,6 +14563,29 @@ func TestDecodeInvalidData(t *testing.T) { 0xf6, }, }, + { + name: "nil element type in inclusiverange type value", + data: []byte{ + // language=edn, format=ccf + // 130([137(41), 194([null])]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagInclusiveRangeTypeValue, + // array, 1 element follow + 0x81, + // null + 0xf6, + }, + }, { name: "nil field type in struct type value", data: []byte{ From b1d81faf8531493b3897abdd3b29808d4a3fbb50 Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Sat, 12 Aug 2023 21:48:59 +0530 Subject: [PATCH 028/121] Remove redundant error from return type --- encoding/ccf/ccf_test.go | 25 +++++++++++++++++++++++++ encoding/ccf/decode.go | 5 +---- encoding/json/decode.go | 11 ++++++----- encoding/json/encoding_test.go | 2 +- runtime/convertValues.go | 5 +---- values.go | 4 ++-- 6 files changed, 36 insertions(+), 16 deletions(-) diff --git a/encoding/ccf/ccf_test.go b/encoding/ccf/ccf_test.go index b550f32f01..682c38ebe5 100644 --- a/encoding/ccf/ccf_test.go +++ b/encoding/ccf/ccf_test.go @@ -14563,6 +14563,31 @@ func TestDecodeInvalidData(t *testing.T) { 0xf6, }, }, + { + name: "more than one element type in inclusiverange type value", + data: []byte{ + // language=edn, format=ccf + // 130([137(41), 194([null, null])]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagInclusiveRangeTypeValue, + // array, 2 elements follow, expected one + 0x82, + // null + 0xf6, + // null + 0xf6, + }, + }, { name: "nil element type in inclusiverange type value", data: []byte{ diff --git a/encoding/ccf/decode.go b/encoding/ccf/decode.go index 8d6d3fb8cf..800831e2b3 100644 --- a/encoding/ccf/decode.go +++ b/encoding/ccf/decode.go @@ -1357,15 +1357,12 @@ func (d *Decoder) decodeInclusiveRange(typ *cadence.InclusiveRangeType, types *c return nil, err } - v, err := cadence.NewMeteredInclusiveRange( + v := cadence.NewMeteredInclusiveRange( d.gauge, start, end, step, ) - if err != nil { - return nil, err - } return v.WithType(typ), nil } diff --git a/encoding/json/decode.go b/encoding/json/decode.go index 3a9abca48c..0985f5f7a0 100644 --- a/encoding/json/decode.go +++ b/encoding/json/decode.go @@ -879,18 +879,19 @@ func (d *Decoder) decodeInclusiveRange(valueJSON any) cadence.InclusiveRange { end := obj.GetValue(d, endKey) step := obj.GetValue(d, stepKey) - value, err := cadence.NewMeteredInclusiveRange( + value := cadence.NewMeteredInclusiveRange( d.gauge, start, end, step, ) - if err != nil { - panic(errors.NewDefaultUserError("invalid InclusiveRange: %w", err)) - } + // TODO: Ensure that start, end and step have the same type. - return value + return value.WithType(cadence.NewMeteredInclusiveRangeType( + d.gauge, + start.Type(), + )) } func (d *Decoder) decodePath(valueJSON any) cadence.Path { diff --git a/encoding/json/encoding_test.go b/encoding/json/encoding_test.go index f00e6f71bf..42dd71242c 100644 --- a/encoding/json/encoding_test.go +++ b/encoding/json/encoding_test.go @@ -1458,7 +1458,7 @@ func TestEncodeInclusiveRange(t *testing.T) { cadence.NewInt256(10), cadence.NewInt256(20), cadence.NewInt256(5), - ), + ).WithType(cadence.NewInclusiveRangeType(cadence.NewInt256Type())), // language=json ` { diff --git a/runtime/convertValues.go b/runtime/convertValues.go index 12b8f1cc9f..9ae0822670 100644 --- a/runtime/convertValues.go +++ b/runtime/convertValues.go @@ -666,15 +666,12 @@ func exportCompositeValueAsInclusiveRange( return cadence.InclusiveRange{}, err } - inclusiveRange, err := cadence.NewMeteredInclusiveRange( + inclusiveRange := cadence.NewMeteredInclusiveRange( inter, startValue, endValue, stepValue, ) - if err != nil { - return cadence.InclusiveRange{}, err - } t := exportInclusiveRangeType(inter, inclusiveRangeType, map[sema.TypeID]cadence.Type{}).(*cadence.InclusiveRangeType) return inclusiveRange.WithType(t), err diff --git a/values.go b/values.go index 2995347fb9..386a1eff71 100644 --- a/values.go +++ b/values.go @@ -2074,9 +2074,9 @@ func NewInclusiveRange(start, end, step Value) InclusiveRange { func NewMeteredInclusiveRange( gauge common.MemoryGauge, start, end, step Value, -) (InclusiveRange, error) { +) InclusiveRange { common.UseMemory(gauge, common.CadenceInclusiveRangeValueMemoryUsage) - return NewInclusiveRange(start, end, step), nil + return NewInclusiveRange(start, end, step) } func (InclusiveRange) isValue() {} From cec396697bf8987fb1ac39656b096d5d80607f18 Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Sat, 12 Aug 2023 22:45:09 +0530 Subject: [PATCH 029/121] Return InclusiveRangeType from exportInclusiveRangeType --- runtime/convertTypes.go | 2 +- runtime/convertValues.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/runtime/convertTypes.go b/runtime/convertTypes.go index 128b0f6d67..5fc28bb722 100644 --- a/runtime/convertTypes.go +++ b/runtime/convertTypes.go @@ -408,7 +408,7 @@ func exportInclusiveRangeType( gauge common.MemoryGauge, t *sema.InclusiveRangeType, results map[sema.TypeID]cadence.Type, -) cadence.Type { +) *cadence.InclusiveRangeType { convertedMemberType := ExportMeteredType(gauge, t.MemberType, results) return cadence.NewMeteredInclusiveRangeType( diff --git a/runtime/convertValues.go b/runtime/convertValues.go index 9ae0822670..9d7f2687ec 100644 --- a/runtime/convertValues.go +++ b/runtime/convertValues.go @@ -673,7 +673,7 @@ func exportCompositeValueAsInclusiveRange( stepValue, ) - t := exportInclusiveRangeType(inter, inclusiveRangeType, map[sema.TypeID]cadence.Type{}).(*cadence.InclusiveRangeType) + t := exportInclusiveRangeType(inter, inclusiveRangeType, map[sema.TypeID]cadence.Type{}) return inclusiveRange.WithType(t), err } From 2c9b4a312dab68e47a4871676868d2122ac72f5a Mon Sep 17 00:00:00 2001 From: darkdrag00nv2 <122124396+darkdrag00nv2@users.noreply.github.com> Date: Sat, 12 Aug 2023 22:46:49 +0530 Subject: [PATCH 030/121] Update runtime/tests/checker/range_value_test.go Co-authored-by: Supun Setunga --- runtime/tests/checker/range_value_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/tests/checker/range_value_test.go b/runtime/tests/checker/range_value_test.go index ea4b77e566..8ff21be07e 100644 --- a/runtime/tests/checker/range_value_test.go +++ b/runtime/tests/checker/range_value_test.go @@ -282,7 +282,7 @@ func TestInclusiveRangeConstructionInvalid(t *testing.T) { baseValueActivation := sema.NewVariableActivation(sema.BaseValueActivation) baseValueActivation.DeclareValue(stdlib.InclusiveRangeConstructorFunction) - runInvalidCase := func(t *testing.T, label, code string, expectedErrorTypes []interface{}) { + runInvalidCase := func(t *testing.T, label, code string, expectedErrorTypes []error) { t.Run(label, func(t *testing.T) { t.Parallel() From 184189bf78523d33223012f58981691a90557970 Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Sat, 12 Aug 2023 22:47:50 +0530 Subject: [PATCH 031/121] use []error instead of []interface{} --- runtime/tests/checker/range_value_test.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/runtime/tests/checker/range_value_test.go b/runtime/tests/checker/range_value_test.go index 8ff21be07e..5fa8fb7c2d 100644 --- a/runtime/tests/checker/range_value_test.go +++ b/runtime/tests/checker/range_value_test.go @@ -323,13 +323,13 @@ func TestInclusiveRangeConstructionInvalid(t *testing.T) { t, typeString, fmt.Sprintf("let r = InclusiveRange(%s(1), %s(2))", typeString, differentTypeString), - []interface{}{&sema.TypeParameterTypeMismatchError{}, &sema.TypeMismatchError{}}, + []error{&sema.TypeParameterTypeMismatchError{}, &sema.TypeMismatchError{}}, ) runInvalidCase( t, typeString, fmt.Sprintf("let r = InclusiveRange(%s(1), %s(10), step: %s(2))", typeString, typeString, differentTypeString), - []interface{}{&sema.TypeParameterTypeMismatchError{}, &sema.TypeMismatchError{}}, + []error{&sema.TypeParameterTypeMismatchError{}, &sema.TypeMismatchError{}}, ) // Not enough arguments @@ -337,7 +337,7 @@ func TestInclusiveRangeConstructionInvalid(t *testing.T) { t, typeString, fmt.Sprintf("let r = InclusiveRange(%s(1))", typeString), - []interface{}{&sema.ArgumentCountError{}}, + []error{&sema.ArgumentCountError{}}, ) // Label for step not provided @@ -345,7 +345,7 @@ func TestInclusiveRangeConstructionInvalid(t *testing.T) { t, typeString, fmt.Sprintf("let r = InclusiveRange(%s(1), %s(0), %s(10))", typeString, typeString, typeString), - []interface{}{&sema.MissingArgumentLabelError{}}, + []error{&sema.MissingArgumentLabelError{}}, ) // Label for start and end provided @@ -353,13 +353,13 @@ func TestInclusiveRangeConstructionInvalid(t *testing.T) { t, typeString, fmt.Sprintf("let r = InclusiveRange(start: %s(1), %s(0))", typeString, typeString), - []interface{}{&sema.IncorrectArgumentLabelError{}}, + []error{&sema.IncorrectArgumentLabelError{}}, ) runInvalidCase( t, typeString, fmt.Sprintf("let r = InclusiveRange(%s(1), end: %s(0))", typeString, typeString), - []interface{}{&sema.IncorrectArgumentLabelError{}}, + []error{&sema.IncorrectArgumentLabelError{}}, ) } } From d981a16e2fb6653f048ff1f4a35d235ee71e34d9 Mon Sep 17 00:00:00 2001 From: darkdrag00nv2 <122124396+darkdrag00nv2@users.noreply.github.com> Date: Sat, 12 Aug 2023 22:48:32 +0530 Subject: [PATCH 032/121] Update runtime/stdlib/range.go Co-authored-by: Supun Setunga --- runtime/stdlib/range.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/runtime/stdlib/range.go b/runtime/stdlib/range.go index 191b5909fc..fe10e3530c 100644 --- a/runtime/stdlib/range.go +++ b/runtime/stdlib/range.go @@ -115,9 +115,11 @@ var InclusiveRangeConstructorFunction = NewStandardLibraryFunction( if stepStaticType != startStaticType { panic(interpreter.InclusiveRangeConstructionError{ LocationRange: locationRange, - Message: fmt.Sprintf("step must be of the same type as start and end. start/end: %s and step: %s", + Message: fmt.Sprintf( + "step must be of the same type as start and end. start/end: %s and step: %s", startStaticType, - stepStaticType), + stepStaticType, + ), }) } From 0a9b4f854d475eb7d014503de272b1a93c34199b Mon Sep 17 00:00:00 2001 From: darkdrag00nv2 <122124396+darkdrag00nv2@users.noreply.github.com> Date: Sat, 12 Aug 2023 22:48:55 +0530 Subject: [PATCH 033/121] Update runtime/stdlib/range.go Co-authored-by: Supun Setunga --- runtime/stdlib/range.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/runtime/stdlib/range.go b/runtime/stdlib/range.go index fe10e3530c..f1e35c2b7e 100644 --- a/runtime/stdlib/range.go +++ b/runtime/stdlib/range.go @@ -96,9 +96,11 @@ var InclusiveRangeConstructorFunction = NewStandardLibraryFunction( if startStaticType != endStaticType { panic(interpreter.InclusiveRangeConstructionError{ LocationRange: locationRange, - Message: fmt.Sprintf("start and end are of different types. start: %s and end: %s", + Message: fmt.Sprintf( + "start and end are of different types. start: %s and end: %s", startStaticType, - endStaticType), + endStaticType, + ), }) } From be883f8baff94721a7316e8b77109ebe63cfe496 Mon Sep 17 00:00:00 2001 From: darkdrag00nv2 <122124396+darkdrag00nv2@users.noreply.github.com> Date: Sat, 12 Aug 2023 22:49:38 +0530 Subject: [PATCH 034/121] Update runtime/interpreter/value_range.go Co-authored-by: Supun Setunga --- runtime/interpreter/value_range.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/runtime/interpreter/value_range.go b/runtime/interpreter/value_range.go index a70bb3da61..bcc4d5e043 100644 --- a/runtime/interpreter/value_range.go +++ b/runtime/interpreter/value_range.go @@ -101,7 +101,14 @@ func NewInclusiveRangeValueWithStep( }) } - return createInclusiveRange(interpreter, locationRange, start, end, step, rangeType, rangeSemaType) + return createInclusiveRange( + interpreter, + locationRange, + end, + step, + rangeType, + rangeSemaType, + ) } func createInclusiveRange( From 8440ae7c1143627a1d3ba4e00716c513b4420de2 Mon Sep 17 00:00:00 2001 From: darkdrag00nv2 <122124396+darkdrag00nv2@users.noreply.github.com> Date: Sat, 12 Aug 2023 22:49:55 +0530 Subject: [PATCH 035/121] Update runtime/interpreter/value_range.go Co-authored-by: Supun Setunga --- runtime/interpreter/value_range.go | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/runtime/interpreter/value_range.go b/runtime/interpreter/value_range.go index bcc4d5e043..a90549f418 100644 --- a/runtime/interpreter/value_range.go +++ b/runtime/interpreter/value_range.go @@ -59,7 +59,15 @@ func NewInclusiveRangeValue( step = negatedStep } - return createInclusiveRange(interpreter, locationRange, start, end, step, rangeStaticType, rangeSemaType) + return createInclusiveRange( + interpreter, + locationRange, + start, + end, + step, + rangeStaticType, + rangeSemaType, + ) } // NewInclusiveRangeValue constructs an InclusiveRange value with the provided start, end & step. From 44d4d62ab32d2f2317cca102814a044328dcc3d8 Mon Sep 17 00:00:00 2001 From: darkdrag00nv2 <122124396+darkdrag00nv2@users.noreply.github.com> Date: Sat, 12 Aug 2023 22:50:06 +0530 Subject: [PATCH 036/121] Update runtime/interpreter/value_range.go Co-authored-by: Supun Setunga --- runtime/interpreter/value_range.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/runtime/interpreter/value_range.go b/runtime/interpreter/value_range.go index a90549f418..cc05230cbf 100644 --- a/runtime/interpreter/value_range.go +++ b/runtime/interpreter/value_range.go @@ -47,7 +47,10 @@ func NewInclusiveRangeValue( if elemSemaTy.Tag().BelongsTo(sema.UnsignedIntegerTypeTag) { panic(InclusiveRangeConstructionError{ LocationRange: locationRange, - Message: fmt.Sprintf("step value cannot be negative for unsigned integer type %s", elemSemaTy), + Message: fmt.Sprintf( + "step value cannot be negative for unsigned integer type %s", + elemSemaTy, + ), }) } From 2b31f2c2c5f52cf5586a385ec881c50f229c73d7 Mon Sep 17 00:00:00 2001 From: darkdrag00nv2 <122124396+darkdrag00nv2@users.noreply.github.com> Date: Sat, 12 Aug 2023 22:50:21 +0530 Subject: [PATCH 037/121] Update runtime/interpreter/value.go Co-authored-by: Supun Setunga --- runtime/interpreter/value.go | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/runtime/interpreter/value.go b/runtime/interpreter/value.go index 1be2f3f23d..97f4db7f32 100644 --- a/runtime/interpreter/value.go +++ b/runtime/interpreter/value.go @@ -16297,7 +16297,15 @@ func NewCompositeValueWithStaticType( address common.Address, staticType StaticType, ) *CompositeValue { - value := NewCompositeValue(interpreter, locationRange, location, qualifiedIdentifier, kind, fields, address) + value := NewCompositeValue( + interpreter, + locationRange, + location, + qualifiedIdentifier, + kind, + fields, + address, + ) value.staticType = staticType return value } From e582b6c04b527dcbd4594b9c8b9a02ecee481ce5 Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Sat, 12 Aug 2023 22:56:16 +0530 Subject: [PATCH 038/121] use constructor instead of direct init; also fix compilation error --- runtime/interpreter/value_range.go | 3 ++- runtime/stdlib/range.go | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/runtime/interpreter/value_range.go b/runtime/interpreter/value_range.go index cc05230cbf..58b6887a0c 100644 --- a/runtime/interpreter/value_range.go +++ b/runtime/interpreter/value_range.go @@ -47,7 +47,7 @@ func NewInclusiveRangeValue( if elemSemaTy.Tag().BelongsTo(sema.UnsignedIntegerTypeTag) { panic(InclusiveRangeConstructionError{ LocationRange: locationRange, - Message: fmt.Sprintf( + Message: fmt.Sprintf( "step value cannot be negative for unsigned integer type %s", elemSemaTy, ), @@ -115,6 +115,7 @@ func NewInclusiveRangeValueWithStep( return createInclusiveRange( interpreter, locationRange, + start, end, step, rangeType, diff --git a/runtime/stdlib/range.go b/runtime/stdlib/range.go index f1e35c2b7e..1e90cfde92 100644 --- a/runtime/stdlib/range.go +++ b/runtime/stdlib/range.go @@ -104,7 +104,7 @@ var InclusiveRangeConstructorFunction = NewStandardLibraryFunction( }) } - rangeStaticType := interpreter.InclusiveRangeStaticType{ElementType: startStaticType} + rangeStaticType := interpreter.NewInclusiveRangeStaticType(invocation.Interpreter, startStaticType) rangeSemaType := sema.NewInclusiveRangeType(invocation.Interpreter, invocation.ArgumentTypes[0]) if len(invocation.Arguments) > 2 { From 112114f123c8f4f12e94b32e0d4f8e63b4a752ca Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Sat, 12 Aug 2023 23:03:52 +0530 Subject: [PATCH 039/121] Use TypeValue instead of PathLinkValue in encoding_test --- runtime/interpreter/encoding_test.go | 118 ++++++++++++++------------- 1 file changed, 62 insertions(+), 56 deletions(-) diff --git a/runtime/interpreter/encoding_test.go b/runtime/interpreter/encoding_test.go index 5033fc8d96..aaf9861772 100644 --- a/runtime/interpreter/encoding_test.go +++ b/runtime/interpreter/encoding_test.go @@ -3988,62 +3988,6 @@ func TestEncodeDecodePathLinkValue(t *testing.T) { ) }) - t.Run("inclusiverange, int", func(t *testing.T) { - - t.Parallel() - - value := PathLinkValue{ - TargetPath: publicPathValue, - Type: InclusiveRangeStaticType{ - ElementType: PrimitiveStaticTypeInt, - }, - } - - encoded := assemble( - // tag - 0xd8, CBORTagInclusiveRangeStaticType, - // tag - 0xd8, CBORTagPrimitiveStaticType, - // positive integer 36 - 0x18, 0x24, - ) - - testEncodeDecode(t, - encodeDecodeTest{ - value: value, - encoded: encoded, - }, - ) - }) - - t.Run("inclusiverange, uint256", func(t *testing.T) { - - t.Parallel() - - value := PathLinkValue{ - TargetPath: publicPathValue, - Type: InclusiveRangeStaticType{ - ElementType: PrimitiveStaticTypeUInt256, - }, - } - - encoded := assemble( - // tag - 0xd8, CBORTagInclusiveRangeStaticType, - // tag - 0xd8, CBORTagPrimitiveStaticType, - // positive integer 50 - 0x18, 0x32, - ) - - testEncodeDecode(t, - encodeDecodeTest{ - value: value, - encoded: encoded, - }, - ) - }) - t.Run("restricted", func(t *testing.T) { t.Parallel() @@ -4287,6 +4231,68 @@ func TestEncodeDecodeTypeValue(t *testing.T) { ) }) + t.Run("inclusiverange, int", func(t *testing.T) { + + t.Parallel() + + value := TypeValue{ + Type: InclusiveRangeStaticType{ + ElementType: PrimitiveStaticTypeInt, + }, + } + + encoded := []byte{ + // tag + 0xd8, CBORTagTypeValue, + // array, 1 items follow + 0x81, + // tag + 0xd8, CBORTagInclusiveRangeStaticType, + // tag + 0xd8, CBORTagPrimitiveStaticType, + // positive integer 36 + 0x18, 0x24, + } + + testEncodeDecode(t, + encodeDecodeTest{ + value: value, + encoded: encoded, + }, + ) + }) + + t.Run("inclusiverange, uint256", func(t *testing.T) { + + t.Parallel() + + value := TypeValue{ + Type: InclusiveRangeStaticType{ + ElementType: PrimitiveStaticTypeUInt256, + }, + } + + encoded := []byte{ + // tag + 0xd8, CBORTagTypeValue, + // array, 1 items follow + 0x81, + // tag + 0xd8, CBORTagInclusiveRangeStaticType, + // tag + 0xd8, CBORTagPrimitiveStaticType, + // positive integer 50 + 0x18, 0x32, + } + + testEncodeDecode(t, + encodeDecodeTest{ + value: value, + encoded: encoded, + }, + ) + }) + t.Run("without static type", func(t *testing.T) { t.Parallel() From dec5f5051f29ad5641b21b955f4874628bd8e01c Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Sat, 12 Aug 2023 23:10:14 +0530 Subject: [PATCH 040/121] Extract sequence direction check to a separate function --- runtime/interpreter/value_range.go | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/runtime/interpreter/value_range.go b/runtime/interpreter/value_range.go index 58b6887a0c..6cabe10db3 100644 --- a/runtime/interpreter/value_range.go +++ b/runtime/interpreter/value_range.go @@ -98,8 +98,7 @@ func NewInclusiveRangeValueWithStep( // If start < end, step must be > 0 // If start > end, step must be < 0 // If start == end, step doesn't matter. - if (start.Less(interpreter, end, locationRange) && step.Less(interpreter, zeroValue, locationRange)) || - (start.Greater(interpreter, end, locationRange) && step.Greater(interpreter, zeroValue, locationRange)) { + if isSequenceMovingAwayFromEnd(interpreter, locationRange, start, end, step, zeroValue) { panic(InclusiveRangeConstructionError{ LocationRange: locationRange, @@ -236,6 +235,18 @@ func getFieldAsIntegerValue( ) } +func isSequenceMovingAwayFromEnd( + interpreter *Interpreter, + locationRange LocationRange, + start IntegerValue, + end IntegerValue, + step IntegerValue, + zeroValue IntegerValue, +) BoolValue { + return (start.Less(interpreter, end, locationRange) && step.Less(interpreter, zeroValue, locationRange)) || + (start.Greater(interpreter, end, locationRange) && step.Greater(interpreter, zeroValue, locationRange)) +} + func convertAndAssertIntegerValue(value Value) IntegerValue { integerValue, ok := value.(IntegerValue) if !ok { From 47fc917306cc3b903a2ed705af7ada84ea2215ed Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Sat, 12 Aug 2023 23:27:45 +0530 Subject: [PATCH 041/121] Predefine list of fields and use in conformance check --- runtime/interpreter/value.go | 6 ++---- runtime/sema/type.go | 6 ++++++ 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/runtime/interpreter/value.go b/runtime/interpreter/value.go index 97f4db7f32..431f53d1e2 100644 --- a/runtime/interpreter/value.go +++ b/runtime/interpreter/value.go @@ -17130,11 +17130,9 @@ func (v *CompositeValue) ConformsToStaticType( return false } - startValue := v.GetField(interpreter, locationRange, sema.InclusiveRangeTypeStartFieldName) - endValue := v.GetField(interpreter, locationRange, sema.InclusiveRangeTypeEndFieldName) - stepValue := v.GetField(interpreter, locationRange, sema.InclusiveRangeTypeStepFieldName) + for _, fieldName := range sema.InclusiveRangeTypeFieldNames { + value := v.GetField(interpreter, locationRange, fieldName) - for _, value := range []Value{startValue, endValue, stepValue} { fieldStaticType := value.StaticType(interpreter) if !interpreter.IsSubTypeOfSemaType(fieldStaticType, inclusiveRangeType.MemberType) { diff --git a/runtime/sema/type.go b/runtime/sema/type.go index 23df60db96..c1da3e0c79 100644 --- a/runtime/sema/type.go +++ b/runtime/sema/type.go @@ -5381,6 +5381,12 @@ const inclusiveRangeTypeStepFieldDocString = ` The step size of the InclusiveRange sequence ` +var InclusiveRangeTypeFieldNames = []string{ + InclusiveRangeTypeStartFieldName, + InclusiveRangeTypeEndFieldName, + InclusiveRangeTypeStepFieldName, +} + const InclusiveRangeTypeContainsFunctionName = "contains" const inclusiveRangeTypeContainsFunctionDocString = ` From 6fbc1a5daee1847aed947cb133f4b9e63f5a64d4 Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Sat, 12 Aug 2023 23:33:37 +0530 Subject: [PATCH 042/121] Use type switch for exporting InclusiveRange --- runtime/convertValues.go | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/runtime/convertValues.go b/runtime/convertValues.go index 9d7f2687ec..f2ca4e4039 100644 --- a/runtime/convertValues.go +++ b/runtime/convertValues.go @@ -380,15 +380,19 @@ func exportCompositeValue( } } - compositeType, ok := semaType.(*sema.CompositeType) - if !ok { + switch semaType := semaType.(type) { + case *sema.CompositeType: + break // handled after the type switch. + case *sema.InclusiveRangeType: // InclusiveRange is stored as a CompositeValue but isn't a CompositeType. - inclusiveRangeType, ok := semaType.(*sema.InclusiveRangeType) - if !ok { - panic(errors.NewUnreachableError()) - } + return exportCompositeValueAsInclusiveRange(v, semaType, inter, locationRange, seenReferences) + default: + panic(errors.NewUnreachableError()) + } - return exportCompositeValueAsInclusiveRange(v, inclusiveRangeType, inter, locationRange, seenReferences) + compositeType, ok := semaType.(*sema.CompositeType) + if !ok { + panic(errors.NewUnreachableError()) } // TODO: consider making the results map "global", by moving it up to exportValueWithInterpreter From 1b1f4509dbcc8bdd448bfa54f856f37d57099569 Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Sat, 12 Aug 2023 23:42:09 +0530 Subject: [PATCH 043/121] Use type switch in conformance check --- runtime/convertValues.go | 2 +- runtime/interpreter/value.go | 13 +++++++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/runtime/convertValues.go b/runtime/convertValues.go index f2ca4e4039..c5c776d34f 100644 --- a/runtime/convertValues.go +++ b/runtime/convertValues.go @@ -382,7 +382,7 @@ func exportCompositeValue( switch semaType := semaType.(type) { case *sema.CompositeType: - break // handled after the type switch. + // Continue. case *sema.InclusiveRangeType: // InclusiveRange is stored as a CompositeValue but isn't a CompositeType. return exportCompositeValueAsInclusiveRange(v, semaType, inter, locationRange, seenReferences) diff --git a/runtime/interpreter/value.go b/runtime/interpreter/value.go index 431f53d1e2..c8783a037c 100644 --- a/runtime/interpreter/value.go +++ b/runtime/interpreter/value.go @@ -17063,9 +17063,8 @@ func (v *CompositeValue) ConformsToStaticType( staticType := v.StaticType(interpreter) semaType := interpreter.MustConvertStaticToSemaType(staticType) - // CompositeValue is also used for storing types which aren't CompositeStaticType. - // E.g. InclusiveRange. - if _, ok := staticType.(CompositeStaticType); ok { + switch staticType.(type) { + case CompositeStaticType: compositeType, ok := semaType.(*sema.CompositeType) if !ok || v.Kind != compositeType.Kind || @@ -17124,7 +17123,10 @@ func (v *CompositeValue) ConformsToStaticType( return false } } - } else if _, ok := staticType.(InclusiveRangeStaticType); ok { + + // CompositeValue is also used for storing types which aren't CompositeStaticType. + // E.g. InclusiveRange. + case InclusiveRangeStaticType: inclusiveRangeType, ok := semaType.(*sema.InclusiveRangeType) if !ok { return false @@ -17147,6 +17149,9 @@ func (v *CompositeValue) ConformsToStaticType( return false } } + + default: + return false } return true From 79ff40309f2620cc5a2dfea498ac743c22a4a717 Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Sun, 13 Aug 2023 15:11:30 +0530 Subject: [PATCH 044/121] Minor nits and add test coverage for Import InclusiveRangeType --- encoding/ccf/encode_type.go | 2 +- encoding/json/encoding_test.go | 24 ++++++++++++------------ runtime/convertValues_test.go | 9 +++++++++ 3 files changed, 22 insertions(+), 13 deletions(-) diff --git a/encoding/ccf/encode_type.go b/encoding/ccf/encode_type.go index 9658aebd78..2d12fbefdc 100644 --- a/encoding/ccf/encode_type.go +++ b/encoding/ccf/encode_type.go @@ -321,7 +321,7 @@ func (e *Encoder) encodeInclusiveRangeType( ) } -// encodeDictTypeWithRawTag encodes cadence.InclusiveRangeType +// encodeInclusiveRangeTypeWithRawTag encodes cadence.InclusiveRangeType // with given tag number and encode type function. func (e *Encoder) encodeInclusiveRangeTypeWithRawTag( typ *cadence.InclusiveRangeType, diff --git a/encoding/json/encoding_test.go b/encoding/json/encoding_test.go index 42dd71242c..dbe4fb0f6f 100644 --- a/encoding/json/encoding_test.go +++ b/encoding/json/encoding_test.go @@ -1979,18 +1979,18 @@ func TestEncodeType(t *testing.T) { }, // language=json ` - { - "type": "Type", - "value": { - "staticType": { - "kind": "InclusiveRange", - "element": { - "kind": "Int" - } - } - } - } - `, + { + "type": "Type", + "value": { + "staticType": { + "kind": "InclusiveRange", + "element": { + "kind": "Int" + } + } + } + } + `, ) }) diff --git a/runtime/convertValues_test.go b/runtime/convertValues_test.go index 19defe7350..4c5d6e23fe 100644 --- a/runtime/convertValues_test.go +++ b/runtime/convertValues_test.go @@ -1215,6 +1215,15 @@ func TestImportRuntimeType(t *testing.T) { ValueType: interpreter.PrimitiveStaticTypeInt, }, }, + { + label: "InclusiveRange", + actual: &cadence.InclusiveRangeType{ + ElementType: cadence.IntType{}, + }, + expected: interpreter.InclusiveRangeStaticType{ + ElementType: interpreter.PrimitiveStaticTypeInt, + }, + }, { label: "Reference", actual: &cadence.ReferenceType{ From 59b0457a4d9ce0d9daac1342a1bff88226877c59 Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Tue, 15 Aug 2023 00:18:15 +0530 Subject: [PATCH 045/121] Infer type in importing InclusiveRange, also improve readability of contains implementation --- runtime/convertValues.go | 97 ++++++++++------- runtime/convertValues_test.go | 161 ++++++++++++++++++++++++----- runtime/interpreter/interpreter.go | 1 + runtime/interpreter/value_range.go | 25 +++-- 4 files changed, 215 insertions(+), 69 deletions(-) diff --git a/runtime/convertValues.go b/runtime/convertValues.go index c5c776d34f..b7789c1061 100644 --- a/runtime/convertValues.go +++ b/runtime/convertValues.go @@ -639,12 +639,11 @@ func exportCompositeValueAsInclusiveRange( panic(errors.NewUnreachableError()) } - getField := func(fieldName string) (cadence.Value, error) { + getNonComputedField := func(fieldName string) (cadence.Value, error) { fieldValue := compositeValue.GetField(inter, locationRange, fieldName) - if fieldValue == nil && compositeValue.ComputedFields != nil { - if computedField, ok := compositeValue.ComputedFields[fieldName]; ok { - fieldValue = computedField(inter, locationRange) - } + if fieldValue == nil { + // Bug if the field is absent. + panic(errors.NewUnreachableError()) } return exportValueWithInterpreter( @@ -655,17 +654,17 @@ func exportCompositeValueAsInclusiveRange( ) } - startValue, err := getField(sema.InclusiveRangeTypeStartFieldName) + startValue, err := getNonComputedField(sema.InclusiveRangeTypeStartFieldName) if err != nil { return cadence.InclusiveRange{}, err } - endValue, err := getField(sema.InclusiveRangeTypeEndFieldName) + endValue, err := getNonComputedField(sema.InclusiveRangeTypeEndFieldName) if err != nil { return cadence.InclusiveRange{}, err } - stepValue, err := getField(sema.InclusiveRangeTypeStepFieldName) + stepValue, err := getNonComputedField(sema.InclusiveRangeTypeStepFieldName) if err != nil { return cadence.InclusiveRange{}, err } @@ -1459,48 +1458,68 @@ func (i valueImporter) importInclusiveRangeValue( *interpreter.CompositeValue, error, ) { + // start, end and step. The order matters. + members := make([]interpreter.IntegerValue, 3) + + var memberType sema.Type inclusiveRangeType, ok := expectedType.(*sema.InclusiveRangeType) - if !ok { - return nil, errors.NewDefaultUserError( - "cannot import InclusiveRange: expected InclusiveRangeType, got '%s'", - expectedType.ID(), - ) + if ok { + memberType = inclusiveRangeType.MemberType + + // The inner type must be an integer. + if !memberType.Tag().BelongsTo(sema.IntegerTypeTag) { + return nil, errors.NewDefaultUserError( + "cannot import inclusiverange: member type must be an integer", + ) + } } - expectedMemberType := inclusiveRangeType.MemberType inter := i.inter locationRange := i.locationRange - startValue, err := i.importValue(v.Start, expectedMemberType) - if err != nil { - return nil, err - } - startInteger, ok := startValue.(interpreter.IntegerValue) - if !ok { - return nil, err - } + // import members. + for index, value := range []cadence.Value{v.Start, v.End, v.Step} { + importedValue, err := i.importValue(value, memberType) + if err != nil { + return nil, err + } + importedIntegerValue, ok := importedValue.(interpreter.IntegerValue) + if !ok { + return nil, err + } - stepValue, err := i.importValue(v.Step, expectedMemberType) - if err != nil { - return nil, err + members[index] = importedIntegerValue } - stepInteger, ok := stepValue.(interpreter.IntegerValue) - if !ok { - return nil, err + + // start, end and step. The order matters. + memberTypes := make([]sema.Type, 3) + memberStaticTypes := make([]interpreter.StaticType, 3) + + for i, member := range members { + memberStaticTypes[i] = member.StaticType(inter) + memberType, err := inter.ConvertStaticToSemaType(memberStaticTypes[i]) + if err != nil { + return nil, err + } + memberTypes[i] = memberType } - endValue, err := i.importValue(v.End, expectedMemberType) - if err != nil { - return nil, err + // start, end and step must have the same static type. + if memberStaticTypes[0] != memberStaticTypes[1] || memberStaticTypes[0] != memberStaticTypes[2] { + return nil, errors.NewDefaultUserError( + "cannot import inclusiverange: start, end and step must be of the same type", + ) } - endInteger, ok := endValue.(interpreter.IntegerValue) - if !ok { - return nil, err + + if inclusiveRangeType == nil { + inclusiveRangeType = sema.NewInclusiveRangeType( + inter, + memberTypes[0], + ) } - staticType := interpreter.ConvertSemaToStaticType(inter, inclusiveRangeType) - inclusiveRangeStaticType, ok := staticType.(interpreter.InclusiveRangeStaticType) + inclusiveRangeStaticType, ok := interpreter.ConvertSemaToStaticType(inter, inclusiveRangeType).(interpreter.InclusiveRangeStaticType) if !ok { panic(errors.NewUnreachableError()) } @@ -1508,9 +1527,9 @@ func (i valueImporter) importInclusiveRangeValue( return interpreter.NewInclusiveRangeValueWithStep( inter, locationRange, - startInteger, - endInteger, - stepInteger, + members[0], + members[1], + members[2], inclusiveRangeStaticType, inclusiveRangeType, ), nil diff --git a/runtime/convertValues_test.go b/runtime/convertValues_test.go index 4c5d6e23fe..edff301317 100644 --- a/runtime/convertValues_test.go +++ b/runtime/convertValues_test.go @@ -1505,35 +1505,127 @@ func TestImportInclusiveRangeValue(t *testing.T) { t.Parallel() - value := cadence.NewInclusiveRange(cadence.NewInt(10), cadence.NewInt(-10), cadence.NewInt(-2)) + t.Run("simple - InclusiveRange", func(t *testing.T) { + t.Parallel() - inter := newTestInterpreter(t) + value := cadence.NewInclusiveRange(cadence.NewInt(10), cadence.NewInt(-10), cadence.NewInt(-2)) - actual, err := ImportValue( - inter, - interpreter.EmptyLocationRange, - nil, - value, - sema.NewInclusiveRangeType(inter, sema.IntType), - ) - require.NoError(t, err) + inter := newTestInterpreter(t) - AssertValuesEqual( - t, - inter, - interpreter.NewInclusiveRangeValueWithStep( + actual, err := ImportValue( inter, interpreter.EmptyLocationRange, - interpreter.NewIntValueFromInt64(inter, 10), - interpreter.NewIntValueFromInt64(inter, -10), - interpreter.NewIntValueFromInt64(inter, -2), - interpreter.InclusiveRangeStaticType{ - ElementType: interpreter.PrimitiveStaticTypeInt, - }, - sema.NewInclusiveRangeType(nil, sema.IntType), - ), - actual, - ) + nil, + value, + sema.NewInclusiveRangeType(inter, sema.IntType), + ) + require.NoError(t, err) + + AssertValuesEqual( + t, + inter, + interpreter.NewInclusiveRangeValueWithStep( + inter, + interpreter.EmptyLocationRange, + interpreter.NewIntValueFromInt64(inter, 10), + interpreter.NewIntValueFromInt64(inter, -10), + interpreter.NewIntValueFromInt64(inter, -2), + interpreter.InclusiveRangeStaticType{ + ElementType: interpreter.PrimitiveStaticTypeInt, + }, + sema.NewInclusiveRangeType(nil, sema.IntType), + ), + actual, + ) + }) + + t.Run("import with broader type - AnyStruct", func(t *testing.T) { + t.Parallel() + + value := cadence.NewInclusiveRange(cadence.NewInt(10), cadence.NewInt(-10), cadence.NewInt(-2)) + + inter := newTestInterpreter(t) + + actual, err := ImportValue( + inter, + interpreter.EmptyLocationRange, + nil, + value, + sema.AnyStructType, + ) + require.NoError(t, err) + + AssertValuesEqual( + t, + inter, + interpreter.NewInclusiveRangeValueWithStep( + inter, + interpreter.EmptyLocationRange, + interpreter.NewIntValueFromInt64(inter, 10), + interpreter.NewIntValueFromInt64(inter, -10), + interpreter.NewIntValueFromInt64(inter, -2), + interpreter.InclusiveRangeStaticType{ + ElementType: interpreter.PrimitiveStaticTypeInt, + }, + sema.NewInclusiveRangeType(nil, sema.IntType), + ), + actual, + ) + }) + + t.Run("invalid type - InclusiveRange", func(t *testing.T) { + t.Parallel() + + value := cadence.NewInclusiveRange(cadence.NewInt(10), cadence.NewInt(-10), cadence.NewInt(-2)) + + inter := newTestInterpreter(t) + + _, err := ImportValue( + inter, + interpreter.EmptyLocationRange, + nil, + value, + sema.NewInclusiveRangeType(inter, sema.AnyStructType), + ) + + RequireError(t, err) + assertUserError(t, err) + + var userError errors.DefaultUserError + require.ErrorAs(t, err, &userError) + require.Contains( + t, + userError.Error(), + "cannot import inclusiverange: member type must be an integer", + ) + }) + + t.Run("mismatched static types", func(t *testing.T) { + t.Parallel() + + value := cadence.NewInclusiveRange(cadence.NewInt(10), cadence.NewUInt(100), cadence.NewInt(-2)) + + inter := newTestInterpreter(t) + + _, err := ImportValue( + inter, + interpreter.EmptyLocationRange, + nil, + value, + sema.AnyStructType, + ) + + RequireError(t, err) + assertUserError(t, err) + + var userError errors.DefaultUserError + require.ErrorAs(t, err, &userError) + require.Contains( + t, + userError.Error(), + "cannot import inclusiverange: start, end and step must be of the same type", + ) + }) } func TestExportStructValue(t *testing.T) { @@ -2835,6 +2927,17 @@ func TestRuntimeArgumentPassing(t *testing.T) { ElementType: cadence.StringType{}, }), }, + { + label: "InclusiveRange", + typeSignature: "InclusiveRange", + exportedValue: cadence.NewInclusiveRange( + cadence.NewUInt128(1), + cadence.NewUInt128(500), + cadence.NewUInt128(25), + ).WithType(&cadence.InclusiveRangeType{ + ElementType: cadence.UInt128Type{}, + }), + }, { label: "Int", typeSignature: "Int", @@ -3515,6 +3618,16 @@ func TestRuntimeMalformedArgumentPassing(t *testing.T) { }), expectedInvalidEntryPointArgumentErrType: &MalformedValueError{}, }, + { + label: "Malformed inclusiverange", + typeSignature: "InclusiveRange", + exportedValue: cadence.NewInclusiveRange( + cadence.NewUInt(1), + cadence.NewUInt(10), + cadence.NewUInt(3), + ), + expectedInvalidEntryPointArgumentErrType: &MalformedValueError{}, + }, } testArgumentPassing := func(test argumentPassingTest) { diff --git a/runtime/interpreter/interpreter.go b/runtime/interpreter/interpreter.go index f74229b8e5..aa0543bd7c 100644 --- a/runtime/interpreter/interpreter.go +++ b/runtime/interpreter/interpreter.go @@ -261,6 +261,7 @@ var _ ast.ExpressionVisitor[Value] = &Interpreter{} // It is reused across all interpreters. var BaseActivation *VariableActivation +// Question: Does it need a mutex? var cachedIntegerValues map[StaticType]map[int8]IntegerValue func init() { diff --git a/runtime/interpreter/value_range.go b/runtime/interpreter/value_range.go index 6cabe10db3..56f6965a52 100644 --- a/runtime/interpreter/value_range.go +++ b/runtime/interpreter/value_range.go @@ -27,6 +27,7 @@ import ( ) // NewInclusiveRangeValue constructs an InclusiveRange value with the provided start, end with default value of step. +// NOTE: Assumes that the values start and end are of the same static type. func NewInclusiveRangeValue( interpreter *Interpreter, locationRange LocationRange, @@ -74,6 +75,7 @@ func NewInclusiveRangeValue( } // NewInclusiveRangeValue constructs an InclusiveRange value with the provided start, end & step. +// NOTE: Assumes that the values start, end and step are of the same static type. func NewInclusiveRangeValueWithStep( interpreter *Interpreter, locationRange LocationRange, @@ -84,7 +86,7 @@ func NewInclusiveRangeValueWithStep( rangeSemaType *sema.InclusiveRangeType, ) *CompositeValue { - zeroValue := GetValueForIntegerType(0, rangeType.ElementType) + zeroValue := GetValueForIntegerType(0, start.StaticType(interpreter)) // Validate that the step is non-zero. if step.Equal(interpreter, locationRange, zeroValue) { @@ -198,11 +200,8 @@ func rangeContains( return TrueValue } - greaterThanStart := needleValue.Greater(interpreter, start, locationRange) - greaterThanend := needleValue.Greater(interpreter, end, locationRange) - - if greaterThanStart == greaterThanend { - // If needle is greater or smaller than both start & end, then it is outside the range. + // Exclusive check since we already checked for boundaries above. + if !isNeedleBetweenStartEndExclusive(interpreter, locationRange, needleValue, start, end) { result = false } else { // needle is in between start and end. @@ -235,6 +234,20 @@ func getFieldAsIntegerValue( ) } +func isNeedleBetweenStartEndExclusive( + interpreter *Interpreter, + locationRange LocationRange, + needleValue IntegerValue, + start IntegerValue, + end IntegerValue, +) bool { + greaterThanStart := needleValue.Greater(interpreter, start, locationRange) + greaterThanEnd := needleValue.Greater(interpreter, end, locationRange) + + // needle is in between start and end values if is greater than one and smaller than the other. + return bool(greaterThanStart) != bool(greaterThanEnd) +} + func isSequenceMovingAwayFromEnd( interpreter *Interpreter, locationRange LocationRange, From 16ddd417962e66684186991e759f859937e9155a Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Sun, 20 Aug 2023 19:03:40 +0530 Subject: [PATCH 046/121] Refactor InclusiveRange import flow and add more test cases --- encoding/json/decode.go | 2 - runtime/convertValues.go | 50 ++++++------ runtime/convertValues_test.go | 31 +------- runtime/interpreter/value.go | 6 +- runtime/program_params_validation_test.go | 93 +++++++++++++++++++++++ 5 files changed, 122 insertions(+), 60 deletions(-) diff --git a/encoding/json/decode.go b/encoding/json/decode.go index 0985f5f7a0..4cbd44d396 100644 --- a/encoding/json/decode.go +++ b/encoding/json/decode.go @@ -886,8 +886,6 @@ func (d *Decoder) decodeInclusiveRange(valueJSON any) cadence.InclusiveRange { step, ) - // TODO: Ensure that start, end and step have the same type. - return value.WithType(cadence.NewMeteredInclusiveRangeType( d.gauge, start.Type(), diff --git a/runtime/convertValues.go b/runtime/convertValues.go index b7789c1061..d6d89ab804 100644 --- a/runtime/convertValues.go +++ b/runtime/convertValues.go @@ -1458,26 +1458,20 @@ func (i valueImporter) importInclusiveRangeValue( *interpreter.CompositeValue, error, ) { - // start, end and step. The order matters. - members := make([]interpreter.IntegerValue, 3) var memberType sema.Type inclusiveRangeType, ok := expectedType.(*sema.InclusiveRangeType) if ok { memberType = inclusiveRangeType.MemberType - - // The inner type must be an integer. - if !memberType.Tag().BelongsTo(sema.IntegerTypeTag) { - return nil, errors.NewDefaultUserError( - "cannot import inclusiverange: member type must be an integer", - ) - } } inter := i.inter locationRange := i.locationRange + // start, end and step. The order matters. + members := make([]interpreter.IntegerValue, 3) + // import members. for index, value := range []cadence.Value{v.Start, v.End, v.Step} { importedValue, err := i.importValue(value, memberType) @@ -1486,36 +1480,24 @@ func (i valueImporter) importInclusiveRangeValue( } importedIntegerValue, ok := importedValue.(interpreter.IntegerValue) if !ok { - return nil, err + return nil, errors.NewDefaultUserError( + "cannot import inclusiverange: start, end and step must be integers", + ) } members[index] = importedIntegerValue } - // start, end and step. The order matters. - memberTypes := make([]sema.Type, 3) - memberStaticTypes := make([]interpreter.StaticType, 3) - - for i, member := range members { - memberStaticTypes[i] = member.StaticType(inter) - memberType, err := inter.ConvertStaticToSemaType(memberStaticTypes[i]) + if inclusiveRangeType == nil { + memberSemaType, err := inter.ConvertStaticToSemaType(members[0].StaticType(inter)) if err != nil { return nil, err } - memberTypes[i] = memberType - } - - // start, end and step must have the same static type. - if memberStaticTypes[0] != memberStaticTypes[1] || memberStaticTypes[0] != memberStaticTypes[2] { - return nil, errors.NewDefaultUserError( - "cannot import inclusiverange: start, end and step must be of the same type", - ) - } - if inclusiveRangeType == nil { + memberType = memberSemaType inclusiveRangeType = sema.NewInclusiveRangeType( inter, - memberTypes[0], + memberType, ) } @@ -1524,6 +1506,18 @@ func (i valueImporter) importInclusiveRangeValue( panic(errors.NewUnreachableError()) } + // Ensure that start, end and step have the same static type. + // Usually this validation would be done outside of this function in ConformsToStaticType but + // we do it here because the NewInclusiveRangeValueWithStep constructor performs validations + // which involve comparisons between these values and hence they need to be of the same static + // type. + if members[0].StaticType(inter) != members[1].StaticType(inter) || + members[0].StaticType(inter) != members[2].StaticType(inter) { + return nil, errors.NewDefaultUserError( + "cannot import inclusiverange: start, end and step must be of the same type", + ) + } + return interpreter.NewInclusiveRangeValueWithStep( inter, locationRange, diff --git a/runtime/convertValues_test.go b/runtime/convertValues_test.go index edff301317..001fec7eb7 100644 --- a/runtime/convertValues_test.go +++ b/runtime/convertValues_test.go @@ -1573,37 +1573,10 @@ func TestImportInclusiveRangeValue(t *testing.T) { ) }) - t.Run("invalid type - InclusiveRange", func(t *testing.T) { + t.Run("invalid - mixed types", func(t *testing.T) { t.Parallel() - value := cadence.NewInclusiveRange(cadence.NewInt(10), cadence.NewInt(-10), cadence.NewInt(-2)) - - inter := newTestInterpreter(t) - - _, err := ImportValue( - inter, - interpreter.EmptyLocationRange, - nil, - value, - sema.NewInclusiveRangeType(inter, sema.AnyStructType), - ) - - RequireError(t, err) - assertUserError(t, err) - - var userError errors.DefaultUserError - require.ErrorAs(t, err, &userError) - require.Contains( - t, - userError.Error(), - "cannot import inclusiverange: member type must be an integer", - ) - }) - - t.Run("mismatched static types", func(t *testing.T) { - t.Parallel() - - value := cadence.NewInclusiveRange(cadence.NewInt(10), cadence.NewUInt(100), cadence.NewInt(-2)) + value := cadence.NewInclusiveRange(cadence.NewInt(10), cadence.NewUInt(100), cadence.NewUInt64(1)) inter := newTestInterpreter(t) diff --git a/runtime/interpreter/value.go b/runtime/interpreter/value.go index c8783a037c..155ea1594e 100644 --- a/runtime/interpreter/value.go +++ b/runtime/interpreter/value.go @@ -17132,12 +17132,16 @@ func (v *CompositeValue) ConformsToStaticType( return false } + expectedMemberStaticType := ConvertSemaToStaticType(interpreter, inclusiveRangeType.MemberType) for _, fieldName := range sema.InclusiveRangeTypeFieldNames { value := v.GetField(interpreter, locationRange, fieldName) fieldStaticType := value.StaticType(interpreter) - if !interpreter.IsSubTypeOfSemaType(fieldStaticType, inclusiveRangeType.MemberType) { + // InclusiveRange is non-covariant. + // For e.g. we disallow assigning InclusiveRange to an InclusiveRange. + // Hence we do an exact equality check instead of a sub-type check. + if fieldStaticType != expectedMemberStaticType { return false } diff --git a/runtime/program_params_validation_test.go b/runtime/program_params_validation_test.go index a8781851dc..57fe9ab695 100644 --- a/runtime/program_params_validation_test.go +++ b/runtime/program_params_validation_test.go @@ -285,6 +285,99 @@ func TestRuntimeScriptParameterTypeValidation(t *testing.T) { assert.NoError(t, err) }) + t.Run("InclusiveRange", func(t *testing.T) { + t.Parallel() + + script := ` + pub fun main(arg: InclusiveRange) { + } + ` + + err := executeScript( + t, + script, + cadence.NewInclusiveRange(cadence.NewInt16(1), cadence.NewInt16(2), cadence.NewInt16(1)), + ) + + assert.NoError(t, err) + }) + + t.Run("InclusiveRange as AnyStruct", func(t *testing.T) { + t.Parallel() + + script := ` + pub fun main(arg: AnyStruct) { + } + ` + + err := executeScript( + t, + script, + cadence.NewInclusiveRange( + cadence.NewUInt16(1), + cadence.NewUInt16(2), + cadence.NewUInt16(1), + ), + ) + + assert.NoError(t, err) + }) + + // Since InclusiveRange isn't covariant. + t.Run("Invalid InclusiveRange", func(t *testing.T) { + t.Parallel() + + script := ` + pub fun main(arg: InclusiveRange) { + } + ` + + err := executeScript( + t, + script, + cadence.NewInclusiveRange(cadence.NewInt16(1), cadence.NewInt16(2), cadence.NewInt16(1)), + ) + + var entryPointErr *InvalidEntryPointArgumentError + require.ErrorAs(t, err, &entryPointErr) + }) + + t.Run("Invalid InclusiveRange with mixed value types", func(t *testing.T) { + t.Parallel() + + script := ` + pub fun main(arg: InclusiveRange) { + } + ` + + err := executeScript( + t, + script, + cadence.NewInclusiveRange(cadence.NewInt16(1), cadence.NewUInt(2), cadence.NewUInt(1)), + ) + + var entryPointErr *InvalidEntryPointArgumentError + require.ErrorAs(t, err, &entryPointErr) + }) + + t.Run("Invalid InclusiveRange with mixed value types", func(t *testing.T) { + t.Parallel() + + script := ` + pub fun main(arg: InclusiveRange) { + } + ` + + err := executeScript( + t, + script, + cadence.NewInclusiveRange(cadence.NewInt16(1), cadence.NewUInt(2), cadence.NewUInt(1)), + ) + + var entryPointErr *InvalidEntryPointArgumentError + require.ErrorAs(t, err, &entryPointErr) + }) + t.Run("Capability", func(t *testing.T) { t.Parallel() From d43a0c5dde0f5c0cf16ca75b1fa0ea7706b36164 Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Sun, 20 Aug 2023 19:32:22 +0530 Subject: [PATCH 047/121] Add tests for missing failure scenario --- runtime/convertValues_test.go | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/runtime/convertValues_test.go b/runtime/convertValues_test.go index 001fec7eb7..4f49219ae3 100644 --- a/runtime/convertValues_test.go +++ b/runtime/convertValues_test.go @@ -1599,6 +1599,36 @@ func TestImportInclusiveRangeValue(t *testing.T) { "cannot import inclusiverange: start, end and step must be of the same type", ) }) + + t.Run("invalid - InclusiveRange", func(t *testing.T) { + t.Parallel() + + strValue, err := cadence.NewString("anything") + require.NoError(t, err) + + value := cadence.NewInclusiveRange(strValue, strValue, strValue) + + inter := newTestInterpreter(t) + + _, err = ImportValue( + inter, + interpreter.EmptyLocationRange, + nil, + value, + sema.StringType, + ) + + RequireError(t, err) + assertUserError(t, err) + + var userError errors.DefaultUserError + require.ErrorAs(t, err, &userError) + require.Contains( + t, + userError.Error(), + "cannot import inclusiverange: start, end and step must be integers", + ) + }) } func TestExportStructValue(t *testing.T) { From e94c03fcbc9c92cab137022ff0dec82086dafa83 Mon Sep 17 00:00:00 2001 From: darkdrag00nv2 <122124396+darkdrag00nv2@users.noreply.github.com> Date: Fri, 25 Aug 2023 15:45:48 +0530 Subject: [PATCH 048/121] Fix comment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bastian Müller --- runtime/interpreter/interpreter.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/interpreter/interpreter.go b/runtime/interpreter/interpreter.go index aa0543bd7c..1169fcc117 100644 --- a/runtime/interpreter/interpreter.go +++ b/runtime/interpreter/interpreter.go @@ -2358,7 +2358,7 @@ func (interpreter *Interpreter) WriteStored( return accountStorage.WriteValue(interpreter, key, value) } -// Get the provided int64 value in the required staticType. +// Get the provided int8 value in the required staticType. // Note: Assumes that the provided value fits within the constraints of the staticType. func GetValueForIntegerType(value int8, staticType StaticType) IntegerValue { typedCache, typedOk := cachedIntegerValues[staticType] From c4c13ffa772290846d40ba1189d0b63363194b1f Mon Sep 17 00:00:00 2001 From: darkdrag00nv2 <122124396+darkdrag00nv2@users.noreply.github.com> Date: Fri, 25 Aug 2023 15:46:19 +0530 Subject: [PATCH 049/121] Remove comment. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bastian Müller --- runtime/interpreter/interpreter.go | 1 - 1 file changed, 1 deletion(-) diff --git a/runtime/interpreter/interpreter.go b/runtime/interpreter/interpreter.go index 1169fcc117..13f05d4ade 100644 --- a/runtime/interpreter/interpreter.go +++ b/runtime/interpreter/interpreter.go @@ -261,7 +261,6 @@ var _ ast.ExpressionVisitor[Value] = &Interpreter{} // It is reused across all interpreters. var BaseActivation *VariableActivation -// Question: Does it need a mutex? var cachedIntegerValues map[StaticType]map[int8]IntegerValue func init() { From ac26a8b1b8d405628353005258ce89e29695f2f3 Mon Sep 17 00:00:00 2001 From: darkdrag00nv2 <122124396+darkdrag00nv2@users.noreply.github.com> Date: Fri, 25 Aug 2023 15:46:51 +0530 Subject: [PATCH 050/121] Use `Equal()` instead of `==` and `!=` operators MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bastian Müller --- runtime/stdlib/range.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/stdlib/range.go b/runtime/stdlib/range.go index 1e90cfde92..de4fa41003 100644 --- a/runtime/stdlib/range.go +++ b/runtime/stdlib/range.go @@ -93,7 +93,7 @@ var InclusiveRangeConstructorFunction = NewStandardLibraryFunction( startStaticType := start.StaticType(inter) endStaticType := end.StaticType(inter) - if startStaticType != endStaticType { + if !startStaticType.Equal(endStaticType) { panic(interpreter.InclusiveRangeConstructionError{ LocationRange: locationRange, Message: fmt.Sprintf( From 5b3d81788231e0667e63ad685b1892bffe443782 Mon Sep 17 00:00:00 2001 From: darkdrag00nv2 <122124396+darkdrag00nv2@users.noreply.github.com> Date: Fri, 25 Aug 2023 15:52:42 +0530 Subject: [PATCH 051/121] Extract elementType as a local variable and use. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bastian Müller --- values.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/values.go b/values.go index 386a1eff71..0484d744cd 100644 --- a/values.go +++ b/values.go @@ -2113,18 +2113,19 @@ func (v InclusiveRange) String() string { } if v.fields == nil { + elementType := v.InclusiveRangeType.ElementType v.fields = []Field{ { Identifier: sema.InclusiveRangeTypeStartFieldName, - Type: v.InclusiveRangeType.ElementType, + Type: elementType, }, { Identifier: sema.InclusiveRangeTypeEndFieldName, - Type: v.InclusiveRangeType.ElementType, + Type: velementType, }, { Identifier: sema.InclusiveRangeTypeStepFieldName, - Type: v.InclusiveRangeType.ElementType, + Type: elementType, }, } } From 7cca141905b983e3b7b4ffc731d008249e277657 Mon Sep 17 00:00:00 2001 From: darkdrag00nv2 <122124396+darkdrag00nv2@users.noreply.github.com> Date: Fri, 25 Aug 2023 15:53:42 +0530 Subject: [PATCH 052/121] Compare with empty string instead of calculating length of string MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bastian Müller --- types.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/types.go b/types.go index f6f2059a18..033fb60cf3 100644 --- a/types.go +++ b/types.go @@ -1087,7 +1087,7 @@ func NewMeteredInclusiveRangeType( func (*InclusiveRangeType) isType() {} func (t *InclusiveRangeType) ID() string { - if len(t.typeID) == 0 { + if t.typeID == "" { t.typeID = fmt.Sprintf( "InclusiveRange<%s>", t.ElementType.ID(), From bed0e373311a893c218116ade0a94218c0cf07e5 Mon Sep 17 00:00:00 2001 From: darkdrag00nv2 <122124396+darkdrag00nv2@users.noreply.github.com> Date: Fri, 25 Aug 2023 15:55:03 +0530 Subject: [PATCH 053/121] Rename TestInclusiveRangeConstructionValid -> TestCheckInclusiveRangeConstructionValid MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bastian Müller --- runtime/tests/checker/range_value_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/tests/checker/range_value_test.go b/runtime/tests/checker/range_value_test.go index 5fa8fb7c2d..e563e22dfc 100644 --- a/runtime/tests/checker/range_value_test.go +++ b/runtime/tests/checker/range_value_test.go @@ -34,7 +34,7 @@ type inclusiveRangeConstructionTest struct { s, e, step int64 } -func TestInclusiveRangeConstructionValid(t *testing.T) { +func TestCheckInclusiveRangeConstructionValid(t *testing.T) { t.Parallel() baseValueActivation := sema.NewVariableActivation(sema.BaseValueActivation) From 5b187d57e76d2a5a0731a83942edf754807a8780 Mon Sep 17 00:00:00 2001 From: darkdrag00nv2 <122124396+darkdrag00nv2@users.noreply.github.com> Date: Fri, 25 Aug 2023 15:55:33 +0530 Subject: [PATCH 054/121] Rename TestInclusiveRangeConstructionInvalid -> TestCheckInclusiveRangeConstructionInvalid MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bastian Müller --- runtime/tests/checker/range_value_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/tests/checker/range_value_test.go b/runtime/tests/checker/range_value_test.go index e563e22dfc..c8351968bb 100644 --- a/runtime/tests/checker/range_value_test.go +++ b/runtime/tests/checker/range_value_test.go @@ -276,7 +276,7 @@ func TestCheckInclusiveRangeConstructionValid(t *testing.T) { } } -func TestInclusiveRangeConstructionInvalid(t *testing.T) { +func TestCheckInclusiveRangeConstructionInvalid(t *testing.T) { t.Parallel() baseValueActivation := sema.NewVariableActivation(sema.BaseValueActivation) From 257043ee3321d558c478d8099ff9d9e330cd3aaa Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Fri, 25 Aug 2023 16:09:19 +0530 Subject: [PATCH 055/121] Fix typo and build --- values.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/values.go b/values.go index 0484d744cd..969e350f7b 100644 --- a/values.go +++ b/values.go @@ -2121,7 +2121,7 @@ func (v InclusiveRange) String() string { }, { Identifier: sema.InclusiveRangeTypeEndFieldName, - Type: velementType, + Type: elementType, }, { Identifier: sema.InclusiveRangeTypeStepFieldName, From 1fa90d3f919abd34d29a3f27f5b3ac5d63ce0a0e Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Fri, 25 Aug 2023 16:10:39 +0530 Subject: [PATCH 056/121] Use decodeCBORArrayWithKnownSize --- encoding/ccf/decode.go | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/encoding/ccf/decode.go b/encoding/ccf/decode.go index 800831e2b3..56f1890e29 100644 --- a/encoding/ccf/decode.go +++ b/encoding/ccf/decode.go @@ -1323,20 +1323,12 @@ func (d *Decoder) decodeEnum(typ *cadence.EnumType, types *cadenceTypeByCCFTypeI // // ] func (d *Decoder) decodeInclusiveRange(typ *cadence.InclusiveRangeType, types *cadenceTypeByCCFTypeID) (cadence.Value, error) { - // Decode array length. - n, err := d.dec.DecodeArrayHead() + // Decode array head of length 3. + err := decodeCBORArrayWithKnownSize(d.dec, 3) if err != nil { return nil, err } - if n != 3 { - return nil, fmt.Errorf( - "encoded inclusiverange-value has %d elements (expected %d elements)", - n, - 3, - ) - } - elementType := typ.ElementType // Decode start. From 7e5c801162c0450b73f171ac3c96cd17b6d1cb08 Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Fri, 25 Aug 2023 16:57:48 +0530 Subject: [PATCH 057/121] Use inline type instead of CBOR array in InclusiveRange type --- encoding/ccf/ccf_test.go | 61 +++++-------------------------------- encoding/ccf/decode_type.go | 14 ++------- encoding/ccf/encode.go | 4 +-- encoding/ccf/encode_type.go | 14 ++------- 4 files changed, 14 insertions(+), 79 deletions(-) diff --git a/encoding/ccf/ccf_test.go b/encoding/ccf/ccf_test.go index 682c38ebe5..d9946f5e9b 100644 --- a/encoding/ccf/ccf_test.go +++ b/encoding/ccf/ccf_test.go @@ -5718,7 +5718,7 @@ func TestEncodeInclusiveRange(t *testing.T) { // {"type":"InclusiveRange","value":[{"type":"Int256","value":"10"},{"type":"Int256","value":"20"},{"type":"Int256","value":"5"}]} // // language=edn, format=ccf - // 130([145([137(10)]), [10, 20, 5]]) + // 130([145(137(10)), [10, 20, 5]]) // // language=cbor, format=ccf // tag @@ -5728,8 +5728,6 @@ func TestEncodeInclusiveRange(t *testing.T) { // type (InclusiveRange) // tag 0xd8, ccf.CBORTagInclusiveRangeType, - // array, 1 item follows - 0x81, // tag 0xd8, ccf.CBORTagSimpleType, // Int256 type ID (10) @@ -5772,7 +5770,7 @@ func TestEncodeInclusiveRange(t *testing.T) { // {"type":"InclusiveRange","value":[{"type":"Int8","value":"10"},{"type":"Int8","value":"20"},{"type":"Int8","value":"5"}]} // // language=edn, format=ccf - // 130([145([137(5)]), [10, 20, 5]]) + // 130([145(137(5)), [10, 20, 5]]) // // language=cbor, format=ccf // tag @@ -5782,8 +5780,6 @@ func TestEncodeInclusiveRange(t *testing.T) { // type (InclusiveRange) // tag 0xd8, ccf.CBORTagInclusiveRangeType, - // array, 1 item follows - 0x81, // tag 0xd8, ccf.CBORTagSimpleType, // Int8 type ID (5) @@ -8219,8 +8215,6 @@ func TestEncodeType(t *testing.T) { 0x18, 0x29, // tag 0xd8, ccf.CBORTagInclusiveRangeTypeValue, - // array, 1 element follows - 0x81, // tag 0xd8, ccf.CBORTagSimpleTypeValue, // Int type (4) @@ -14146,7 +14140,7 @@ func TestDecodeInvalidData(t *testing.T) { name: "nil element type in inclusiverange type", data: []byte{ // language=edn, format=ccf - // 130([145([nil]), [10, 20, 5]]) + // 130([145(nil), [10, 20, 5]]) // // language=cbor, format=ccf // tag @@ -14156,8 +14150,6 @@ func TestDecodeInvalidData(t *testing.T) { // type (InclusiveRange) // tag 0xd8, ccf.CBORTagInclusiveRangeType, - // array, 1 item follow - 0x81, // null 0xf6, // array data without inlined type definition @@ -14187,7 +14179,7 @@ func TestDecodeInvalidData(t *testing.T) { name: "invalid array head in inclusiverange value", data: []byte{ // language=edn, format=ccf - // 130([145([4]), [10, 20, 5]]) + // 130([145(4), [10, 20, 5]]) // // language=cbor, format=ccf // tag @@ -14197,8 +14189,6 @@ func TestDecodeInvalidData(t *testing.T) { // type (InclusiveRange) // tag 0xd8, ccf.CBORTagInclusiveRangeType, - // array, 1 item follow - 0x81, // tag 0xd8, ccf.CBORTagSimpleType, // Int type ID (4) @@ -14229,7 +14219,7 @@ func TestDecodeInvalidData(t *testing.T) { name: "incorrect member count (2 instead of 3) in inclusiverange value", data: []byte{ // language=edn, format=ccf - // 130([145([4]), [10, 20, 5]]) + // 130([145(4), [10, 20, 5]]) // // language=cbor, format=ccf // tag @@ -14239,8 +14229,6 @@ func TestDecodeInvalidData(t *testing.T) { // type (InclusiveRange) // tag 0xd8, ccf.CBORTagInclusiveRangeType, - // array, 1 item follow - 0x81, // tag 0xd8, ccf.CBORTagSimpleType, // Int type ID (4) @@ -14266,7 +14254,7 @@ func TestDecodeInvalidData(t *testing.T) { name: "invalid start value in inclusiverange value", data: []byte{ // language=edn, format=ccf - // 130([145([5]), [10, 20, 5]]) + // 130([145(5), [10, 20, 5]]) // // language=cbor, format=ccf // tag @@ -14276,8 +14264,6 @@ func TestDecodeInvalidData(t *testing.T) { // type (InclusiveRange) // tag 0xd8, ccf.CBORTagInclusiveRangeType, - // array, 1 item follow - 0x81, // tag 0xd8, ccf.CBORTagSimpleType, // Int8 type ID (5) @@ -14301,7 +14287,7 @@ func TestDecodeInvalidData(t *testing.T) { name: "invalid end value in inclusiverange value", data: []byte{ // language=edn, format=ccf - // 130([145([5]), [10, 20, 5]]) + // 130([145(5), [10, 20, 5]]) // // language=cbor, format=ccf // tag @@ -14311,8 +14297,6 @@ func TestDecodeInvalidData(t *testing.T) { // type (InclusiveRange) // tag 0xd8, ccf.CBORTagInclusiveRangeType, - // array, 1 item follow - 0x81, // tag 0xd8, ccf.CBORTagSimpleType, // Int8 type ID (5) @@ -14336,7 +14320,7 @@ func TestDecodeInvalidData(t *testing.T) { name: "invalid step value in inclusiverange value", data: []byte{ // language=edn, format=ccf - // 130([145([5]), [10, 20, 5]]) + // 130([145(5), [10, 20, 5]]) // // language=cbor, format=ccf // tag @@ -14346,8 +14330,6 @@ func TestDecodeInvalidData(t *testing.T) { // type (InclusiveRange) // tag 0xd8, ccf.CBORTagInclusiveRangeType, - // array, 1 item follow - 0x81, // tag 0xd8, ccf.CBORTagSimpleType, // Int8 type ID (5) @@ -14563,31 +14545,6 @@ func TestDecodeInvalidData(t *testing.T) { 0xf6, }, }, - { - name: "more than one element type in inclusiverange type value", - data: []byte{ - // language=edn, format=ccf - // 130([137(41), 194([null, null])]) - // - // language=cbor, format=ccf - // tag - 0xd8, ccf.CBORTagTypeAndValue, - // array, 2 elements follow - 0x82, - // tag - 0xd8, ccf.CBORTagSimpleType, - // Meta type ID (41) - 0x18, 0x29, - // tag - 0xd8, ccf.CBORTagInclusiveRangeTypeValue, - // array, 2 elements follow, expected one - 0x82, - // null - 0xf6, - // null - 0xf6, - }, - }, { name: "nil element type in inclusiverange type value", data: []byte{ @@ -14605,8 +14562,6 @@ func TestDecodeInvalidData(t *testing.T) { 0x18, 0x29, // tag 0xd8, ccf.CBORTagInclusiveRangeTypeValue, - // array, 1 element follow - 0x81, // null 0xf6, }, diff --git a/encoding/ccf/decode_type.go b/encoding/ccf/decode_type.go index 3067a143af..3a82648e51 100644 --- a/encoding/ccf/decode_type.go +++ b/encoding/ccf/decode_type.go @@ -445,28 +445,18 @@ func (d *Decoder) decodeDictType( // inclusiverange-type = // // ; cbor-tag-inclusiverange-type -// #6.145([ -// element-type: inline-type -// ]) +// #6.145(inline-type) // // inclusiverange-type-value = // // ; cbor-tag-inclusiverange-type-value -// #6.194([ -// element-type: type-value -// ]) +// #6.194(type-value) // // NOTE: decodeTypeFn is responsible for decoding inline-type or type-value. func (d *Decoder) decodeInclusiveRangeType( types *cadenceTypeByCCFTypeID, decodeTypeFn decodeTypeFn, ) (cadence.Type, error) { - // Decode array head of length 1. - err := decodeCBORArrayWithKnownSize(d.dec, 1) - if err != nil { - return nil, err - } - // element 0: element type (inline-type or type-value) elementType, err := decodeTypeFn(types) if err != nil { diff --git a/encoding/ccf/encode.go b/encoding/ccf/encode.go index 3cf76ba329..9c64f8d174 100644 --- a/encoding/ccf/encode.go +++ b/encoding/ccf/encode.go @@ -1452,9 +1452,7 @@ func (e *Encoder) encodeDictTypeValue(typ *cadence.DictionaryType, visited ccfTy // inclusiverange-type-value = // // ; cbor-tag-inclusiverange-type-value -// #6.194([ -// element-type: type-value -// ]) +// #6.194(type-value) func (e *Encoder) encodeInclusiveRangeTypeValue(typ *cadence.InclusiveRangeType, visited ccfTypeIDByCadenceType) error { rawTagNum := []byte{0xd8, CBORTagInclusiveRangeTypeValue} return e.encodeInclusiveRangeTypeWithRawTag( diff --git a/encoding/ccf/encode_type.go b/encoding/ccf/encode_type.go index 2d12fbefdc..d917bf53ab 100644 --- a/encoding/ccf/encode_type.go +++ b/encoding/ccf/encode_type.go @@ -304,10 +304,8 @@ func (e *Encoder) encodeDictTypeWithRawTag( // language=CDDL // inclusiverange-type = // -// ; cbor-tag-inclusiverange-type -// #6.145([ -// element-type: inline-type -// ]) +// ; cbor-tag-inclusiverange-type +// #6.145(inline-type) func (e *Encoder) encodeInclusiveRangeType( typ *cadence.InclusiveRangeType, tids ccfTypeIDByCadenceType, @@ -335,13 +333,7 @@ func (e *Encoder) encodeInclusiveRangeTypeWithRawTag( return err } - // Encode array head of length 1. - err = e.enc.EncodeArrayHead(1) - if err != nil { - return err - } - - // element 0: element type with given encodeTypeFn + // Encode element type with given encodeTypeFn return encodeTypeFn(typ.ElementType, tids) } From 80e9f2ee903b1c59cdd51e8df7dbd854f3f63633 Mon Sep 17 00:00:00 2001 From: darkdrag00nv2 <122124396+darkdrag00nv2@users.noreply.github.com> Date: Fri, 25 Aug 2023 17:01:44 +0530 Subject: [PATCH 058/121] Use stricter multiplicity annotation. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bastian Müller --- encoding/ccf/encode.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/encoding/ccf/encode.go b/encoding/ccf/encode.go index 9c64f8d174..a2a9600d61 100644 --- a/encoding/ccf/encode.go +++ b/encoding/ccf/encode.go @@ -983,7 +983,7 @@ func encodeAndSortKeyValuePairs( // encodeInclusiveRange encodes cadence.InclusiveRange as // language=CDDL -// inclusiverange-value = [* (key: value, value: value)] +// inclusiverange-value = [3*3 (key: value, value: value)] func (e *Encoder) encodeInclusiveRange(v cadence.InclusiveRange, tids ccfTypeIDByCadenceType) error { staticElementType := v.InclusiveRangeType.ElementType From c7db7325cd1cb3ebc6aeb99a529d37b37eab612d Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Fri, 25 Aug 2023 17:18:22 +0530 Subject: [PATCH 059/121] use r.MemberType directly instead of r.ElementType function --- runtime/sema/type.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/runtime/sema/type.go b/runtime/sema/type.go index c1da3e0c79..6bbbcbcb18 100644 --- a/runtime/sema/type.go +++ b/runtime/sema/type.go @@ -5423,7 +5423,7 @@ func (r *InclusiveRangeType) initializeMemberResolvers() { memoryGauge, r, identifier, - r.ElementType(false), + r.MemberType, inclusiveRangeTypeStartFieldDocString, ) }, @@ -5435,7 +5435,7 @@ func (r *InclusiveRangeType) initializeMemberResolvers() { memoryGauge, r, identifier, - r.ElementType(false), + r.MemberType, inclusiveRangeTypeEndFieldDocString, ) }, @@ -5447,7 +5447,7 @@ func (r *InclusiveRangeType) initializeMemberResolvers() { memoryGauge, r, identifier, - r.ElementType(false), + r.MemberType, inclusiveRangeTypeStepFieldDocString, ) }, @@ -5455,7 +5455,7 @@ func (r *InclusiveRangeType) initializeMemberResolvers() { InclusiveRangeTypeContainsFunctionName: { Kind: common.DeclarationKindFunction, Resolve: func(memoryGauge common.MemoryGauge, identifier string, targetRange ast.Range, report func(error)) *Member { - elementType := r.ElementType(false) + elementType := r.MemberType return NewPublicFunctionMember( memoryGauge, From e2a8436a2ded84d242a2a1a1b914cb9a82f35bb1 Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Fri, 25 Aug 2023 18:47:14 +0530 Subject: [PATCH 060/121] Add TestGetValueForIntegerType --- runtime/tests/interpreter/range_value_test.go | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/runtime/tests/interpreter/range_value_test.go b/runtime/tests/interpreter/range_value_test.go index d18f26764e..e6ce301f1f 100644 --- a/runtime/tests/interpreter/range_value_test.go +++ b/runtime/tests/interpreter/range_value_test.go @@ -467,6 +467,25 @@ func TestInclusiveRange(t *testing.T) { } } +func TestGetValueForIntegerType(t *testing.T) { + + t.Parallel() + + // Ensure that GetValueForIntegerType handles every IntegerType + + for _, integerType := range sema.AllIntegerTypes { + switch integerType { + case sema.IntegerType, sema.SignedIntegerType: + continue + } + + staticType := interpreter.ConvertSemaToStaticType(nil, integerType) + + // Panics if not handled. + _ = interpreter.GetValueForIntegerType(int8(1), staticType) + } +} + func TestInclusiveRangeConstructionInvalid(t *testing.T) { t.Parallel() From 5de89b1839603e417bca7a964ebd380daf40ff13 Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Fri, 25 Aug 2023 21:15:29 +0530 Subject: [PATCH 061/121] Add runtime type tests for InclusiveRangeType --- runtime/tests/checker/runtimetype_test.go | 72 +++++++++++++++++++ runtime/tests/interpreter/runtimetype_test.go | 45 ++++++++++++ 2 files changed, 117 insertions(+) diff --git a/runtime/tests/checker/runtimetype_test.go b/runtime/tests/checker/runtimetype_test.go index a2aea9373f..9a6e4a7d98 100644 --- a/runtime/tests/checker/runtimetype_test.go +++ b/runtime/tests/checker/runtimetype_test.go @@ -852,3 +852,75 @@ func TestCheckCapabilityTypeConstructor(t *testing.T) { }) } } + +func TestCheckInclusiveRangeTypeConstructor(t *testing.T) { + + t.Parallel() + + cases := []struct { + name string + code string + expectedError error + }{ + { + name: "Int", + code: ` + let result = InclusiveRangeType(Type()) + `, + expectedError: nil, + }, + { + name: "UInt16", + code: ` + let result = InclusiveRangeType(Type()) + `, + expectedError: nil, + }, + { + name: "resource", + code: ` + resource R {} + let result = InclusiveRangeType(Type<@R>()) + `, + expectedError: nil, + }, + { + name: "type mismatch", + code: ` + let result = InclusiveRangeType(3) + `, + expectedError: &sema.TypeMismatchError{}, + }, + { + name: "too many args", + code: ` + let result = InclusiveRangeType(Type(), Type()) + `, + expectedError: &sema.ArgumentCountError{}, + }, + { + name: "too few args", + code: ` + let result = InclusiveRangeType() + `, + expectedError: &sema.ArgumentCountError{}, + }, + } + + for _, testCase := range cases { + t.Run(testCase.name, func(t *testing.T) { + checker, err := ParseAndCheck(t, testCase.code) + + if testCase.expectedError == nil { + require.NoError(t, err) + assert.Equal(t, + &sema.OptionalType{Type: sema.MetaType}, + RequireGlobalValue(t, checker.Elaboration, "result"), + ) + } else { + errs := RequireCheckerErrors(t, err, 1) + assert.IsType(t, testCase.expectedError, errs[0]) + } + }) + } +} diff --git a/runtime/tests/interpreter/runtimetype_test.go b/runtime/tests/interpreter/runtimetype_test.go index dfef72fb40..81a39464c6 100644 --- a/runtime/tests/interpreter/runtimetype_test.go +++ b/runtime/tests/interpreter/runtimetype_test.go @@ -753,3 +753,48 @@ func TestInterpretCapabilityType(t *testing.T) { inter.Globals.Get("e").GetValue(), ) } + +func TestInterpretInclusiveRangeType(t *testing.T) { + + t.Parallel() + + inter := parseCheckAndInterpret(t, ` + let a = InclusiveRangeType(Type())! + let b = InclusiveRangeType(Type<&Int>()) + + resource R {} + let c = InclusiveRangeType(Type<@R>()) + let d = InclusiveRangeType(Type()) + + let e = InclusiveRangeType(Type())! + `) + + assert.Equal(t, + interpreter.TypeValue{ + Type: interpreter.InclusiveRangeStaticType{ + ElementType: interpreter.PrimitiveStaticTypeInt, + }, + }, + inter.Globals.Get("a").GetValue(), + ) + + assert.Equal(t, + interpreter.Nil, + inter.Globals.Get("b").GetValue(), + ) + + assert.Equal(t, + interpreter.Nil, + inter.Globals.Get("c").GetValue(), + ) + + assert.Equal(t, + interpreter.Nil, + inter.Globals.Get("d").GetValue(), + ) + + assert.Equal(t, + inter.Globals.Get("a").GetValue(), + inter.Globals.Get("e").GetValue(), + ) +} From f44c8a50a8403d4c71bcaca0722b857e51dea977 Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Sun, 27 Aug 2023 18:07:30 +0530 Subject: [PATCH 062/121] Fix build and tests since we have Arity support now --- runtime/stdlib/range.go | 3 ++- runtime/tests/checker/range_value_test.go | 2 +- runtime/tests/checker/runtimetype_test.go | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/runtime/stdlib/range.go b/runtime/stdlib/range.go index de4fa41003..2f39ee596b 100644 --- a/runtime/stdlib/range.go +++ b/runtime/stdlib/range.go @@ -72,7 +72,8 @@ var inclusiveRangeConstructorFunctionType = func() *sema.FunctionType { MemberType: typeAnnotation.Type, }, ), - RequiredArgumentCount: sema.RequiredArgumentCount(2), + // `step` parameter is optional + Arity: &sema.Arity{Min: 2, Max: 3}, } }() diff --git a/runtime/tests/checker/range_value_test.go b/runtime/tests/checker/range_value_test.go index c8351968bb..964bd001e6 100644 --- a/runtime/tests/checker/range_value_test.go +++ b/runtime/tests/checker/range_value_test.go @@ -337,7 +337,7 @@ func TestCheckInclusiveRangeConstructionInvalid(t *testing.T) { t, typeString, fmt.Sprintf("let r = InclusiveRange(%s(1))", typeString), - []error{&sema.ArgumentCountError{}}, + []error{&sema.InsufficientArgumentsError{}}, ) // Label for step not provided diff --git a/runtime/tests/checker/runtimetype_test.go b/runtime/tests/checker/runtimetype_test.go index 9a6e4a7d98..b97e005474 100644 --- a/runtime/tests/checker/runtimetype_test.go +++ b/runtime/tests/checker/runtimetype_test.go @@ -896,14 +896,14 @@ func TestCheckInclusiveRangeTypeConstructor(t *testing.T) { code: ` let result = InclusiveRangeType(Type(), Type()) `, - expectedError: &sema.ArgumentCountError{}, + expectedError: &sema.ExcessiveArgumentsError{}, }, { name: "too few args", code: ` let result = InclusiveRangeType() `, - expectedError: &sema.ArgumentCountError{}, + expectedError: &sema.InsufficientArgumentsError{}, }, } From a4d4c96d888de388dd735d96ae74c16148cb1c46 Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Sun, 27 Aug 2023 20:37:08 +0530 Subject: [PATCH 063/121] Add runtime to load and call contains --- runtime/tests/interpreter/account_test.go | 52 +++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/runtime/tests/interpreter/account_test.go b/runtime/tests/interpreter/account_test.go index 64fec7448a..a59ad2fe94 100644 --- a/runtime/tests/interpreter/account_test.go +++ b/runtime/tests/interpreter/account_test.go @@ -89,6 +89,8 @@ func testAccount( accountValueDeclaration.Name = "account" valueDeclarations = append(valueDeclarations, accountValueDeclaration) + valueDeclarations = append(valueDeclarations, stdlib.InclusiveRangeConstructorFunction) + if checkerConfig.BaseValueActivation == nil { checkerConfig.BaseValueActivation = sema.BaseValueActivation } @@ -509,6 +511,56 @@ func TestInterpretAuthAccount_load(t *testing.T) { require.Len(t, getAccountValues(), 1) }) }) + + t.Run("inclusiverange with contains", func(t *testing.T) { + + t.Parallel() + + address := interpreter.NewUnmeteredAddressValueFromBytes([]byte{42}) + + inter, getAccountValues := testAccount( + t, + address, + true, + ` + fun save() { + let ir = InclusiveRange(10, 20) + account.save(ir, to: /storage/ir) + } + + fun loadIRAndCallContains(): InclusiveRange? { + let ir = account.load>(from: /storage/ir) + let containsFifteen = ir!.contains(15) + return ir + } + `, + sema.Config{}, + ) + + t.Run("save and load inclusiverange. call contains", func(t *testing.T) { + + // save + + _, err := inter.Invoke("save") + require.NoError(t, err) + + require.Len(t, getAccountValues(), 1) + + // first load + + value, err := inter.Invoke("loadIRAndCallContains") + require.NoError(t, err) + + require.IsType(t, &interpreter.SomeValue{}, value) + + innerValue := value.(*interpreter.SomeValue).InnerValue(inter, interpreter.EmptyLocationRange) + + assert.IsType(t, &interpreter.CompositeValue{}, innerValue) + + // NOTE: check loaded value was removed from storage + require.Len(t, getAccountValues(), 0) + }) + }) } func TestInterpretAuthAccount_copy(t *testing.T) { From 1d5426050c04a4fd8260adcf1a640cde73d9a242 Mon Sep 17 00:00:00 2001 From: darkdrag00nv2 <122124396+darkdrag00nv2@users.noreply.github.com> Date: Wed, 6 Sep 2023 23:34:24 +0530 Subject: [PATCH 064/121] Handle each level of multi-level type separately. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bastian Müller --- runtime/interpreter/interpreter.go | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/runtime/interpreter/interpreter.go b/runtime/interpreter/interpreter.go index 13f05d4ade..f7ead1c07d 100644 --- a/runtime/interpreter/interpreter.go +++ b/runtime/interpreter/interpreter.go @@ -2360,19 +2360,19 @@ func (interpreter *Interpreter) WriteStored( // Get the provided int8 value in the required staticType. // Note: Assumes that the provided value fits within the constraints of the staticType. func GetValueForIntegerType(value int8, staticType StaticType) IntegerValue { - typedCache, typedOk := cachedIntegerValues[staticType] - if typedOk { - val, ok := typedCache[value] - if ok { - return val - } + typeCache, ok := cachedIntegerValues[staticType] + if !ok { + typeCache = make(map[int8]IntegerValue) + cachedIntegerValues[staticType] = typeCache } - val := getValueForIntegerType(value, staticType) - if !typedOk { - cachedIntegerValues[staticType] = make(map[int8]IntegerValue) + val, ok := typeCache[value] + if !ok { + val = getValueForIntegerType(value, staticType) + typeCache[value] = val } - cachedIntegerValues[staticType][value] = val + + return val return val } From 131a163c6b2a0061839b8e416cda51afbb97b5e1 Mon Sep 17 00:00:00 2001 From: darkdrag00nv2 <122124396+darkdrag00nv2@users.noreply.github.com> Date: Wed, 6 Sep 2023 23:38:05 +0530 Subject: [PATCH 065/121] Remove unnecessary nesting. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bastian Müller --- runtime/stdlib/range.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/runtime/stdlib/range.go b/runtime/stdlib/range.go index 2f39ee596b..4f2f54227d 100644 --- a/runtime/stdlib/range.go +++ b/runtime/stdlib/range.go @@ -135,15 +135,15 @@ var InclusiveRangeConstructorFunction = NewStandardLibraryFunction( rangeStaticType, rangeSemaType, ) - } else { - return interpreter.NewInclusiveRangeValue( - inter, - locationRange, - start, - end, - rangeStaticType, - rangeSemaType, - ) } + + return interpreter.NewInclusiveRangeValue( + inter, + locationRange, + start, + end, + rangeStaticType, + rangeSemaType, + ) }, ) From d169d097d485c6fce5272f61cd26864b67a44559 Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Wed, 6 Sep 2023 23:37:27 +0530 Subject: [PATCH 066/121] Extract local variable for invocation.Interpreter --- runtime/interpreter/interpreter.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/runtime/interpreter/interpreter.go b/runtime/interpreter/interpreter.go index f7ead1c07d..285d22e923 100644 --- a/runtime/interpreter/interpreter.go +++ b/runtime/interpreter/interpreter.go @@ -3634,19 +3634,21 @@ var runtimeTypeConstructors = []runtimeTypeConstructor{ panic(errors.NewUnreachableError()) } + inter := invocation.Interpreter + ty := typeValue.Type // InclusiveRanges must hold integers - elemSemaTy := invocation.Interpreter.MustConvertStaticToSemaType(ty) + elemSemaTy := inter.MustConvertStaticToSemaType(ty) if !elemSemaTy.Tag().BelongsTo(sema.IntegerTypeTag) { return Nil } return NewSomeValueNonCopying( - invocation.Interpreter, + inter, NewTypeValue( - invocation.Interpreter, + inter, NewInclusiveRangeStaticType( - invocation.Interpreter, + inter, ty, ), ), From cf5edc82cdcdf41e02887f54b017426c1608349f Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Wed, 6 Sep 2023 23:40:25 +0530 Subject: [PATCH 067/121] Use StaticType.Equal --- runtime/interpreter/value.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/interpreter/value.go b/runtime/interpreter/value.go index 155ea1594e..3fa7c2a821 100644 --- a/runtime/interpreter/value.go +++ b/runtime/interpreter/value.go @@ -17141,7 +17141,7 @@ func (v *CompositeValue) ConformsToStaticType( // InclusiveRange is non-covariant. // For e.g. we disallow assigning InclusiveRange to an InclusiveRange. // Hence we do an exact equality check instead of a sub-type check. - if fieldStaticType != expectedMemberStaticType { + if !fieldStaticType.Equal(expectedMemberStaticType) { return false } From e47dbada4bffc95a40a74a0eb4a5bc7baff13f25 Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Thu, 7 Sep 2023 22:56:22 +0530 Subject: [PATCH 068/121] Add comment on not metering memory usage of getValueForIntegerType --- runtime/interpreter/interpreter.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/runtime/interpreter/interpreter.go b/runtime/interpreter/interpreter.go index 285d22e923..7529527f2f 100644 --- a/runtime/interpreter/interpreter.go +++ b/runtime/interpreter/interpreter.go @@ -2373,9 +2373,13 @@ func GetValueForIntegerType(value int8, staticType StaticType) IntegerValue { } return val - return val } +// It is important to not meter the memory usage in this function, as it would lead to +// non-determinism as the values produced by this function are cached. +// It could happen that on some execution nodes the value might be cached due to executing a +// transaction or script that needed the value previously, while on other execution nodes it might +// not be cached yet. func getValueForIntegerType(value int8, staticType StaticType) IntegerValue { switch staticType { case PrimitiveStaticTypeInt: From 5d1b13fde87e283d7ef04fc7199d84a5810ab9ea Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Thu, 7 Sep 2023 23:02:22 +0530 Subject: [PATCH 069/121] Move GetValueForIntegerType to a new file --- runtime/interpreter/integer.go | 100 +++++++++++++++++++++++++++++ runtime/interpreter/interpreter.go | 77 ---------------------- 2 files changed, 100 insertions(+), 77 deletions(-) create mode 100644 runtime/interpreter/integer.go diff --git a/runtime/interpreter/integer.go b/runtime/interpreter/integer.go new file mode 100644 index 0000000000..62bfa3e372 --- /dev/null +++ b/runtime/interpreter/integer.go @@ -0,0 +1,100 @@ +/* + * Cadence - The resource-oriented smart contract programming language + * + * Copyright Dapper Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package interpreter + +import "github.com/onflow/cadence/runtime/errors" + +var cachedIntegerValues map[StaticType]map[int8]IntegerValue + +func init() { + cachedIntegerValues = make(map[StaticType]map[int8]IntegerValue) +} + +// Get the provided int8 value in the required staticType. +// Note: Assumes that the provided value fits within the constraints of the staticType. +func GetValueForIntegerType(value int8, staticType StaticType) IntegerValue { + typeCache, ok := cachedIntegerValues[staticType] + if !ok { + typeCache = make(map[int8]IntegerValue) + cachedIntegerValues[staticType] = typeCache + } + + val, ok := typeCache[value] + if !ok { + val = getValueForIntegerType(value, staticType) + typeCache[value] = val + } + + return val +} + +// It is important to not meter the memory usage in this function, as it would lead to +// non-determinism as the values produced by this function are cached. +// It could happen that on some execution nodes the value might be cached due to executing a +// transaction or script that needed the value previously, while on other execution nodes it might +// not be cached yet. +func getValueForIntegerType(value int8, staticType StaticType) IntegerValue { + switch staticType { + case PrimitiveStaticTypeInt: + return NewUnmeteredIntValueFromInt64(int64(value)) + case PrimitiveStaticTypeInt8: + return NewUnmeteredInt8Value(value) + case PrimitiveStaticTypeInt16: + return NewUnmeteredInt16Value(int16(value)) + case PrimitiveStaticTypeInt32: + return NewUnmeteredInt32Value(int32(value)) + case PrimitiveStaticTypeInt64: + return NewUnmeteredInt64Value(int64(value)) + case PrimitiveStaticTypeInt128: + return NewUnmeteredInt128ValueFromInt64(int64(value)) + case PrimitiveStaticTypeInt256: + return NewUnmeteredInt256ValueFromInt64(int64(value)) + + case PrimitiveStaticTypeUInt: + return NewUnmeteredUIntValueFromUint64(uint64(value)) + case PrimitiveStaticTypeUInt8: + return NewUnmeteredUInt8Value(uint8(value)) + case PrimitiveStaticTypeUInt16: + return NewUnmeteredUInt16Value(uint16(value)) + case PrimitiveStaticTypeUInt32: + return NewUnmeteredUInt32Value(uint32(value)) + case PrimitiveStaticTypeUInt64: + return NewUnmeteredUInt64Value(uint64(value)) + case PrimitiveStaticTypeUInt128: + return NewUnmeteredUInt128ValueFromUint64(uint64(value)) + case PrimitiveStaticTypeUInt256: + return NewUnmeteredUInt256ValueFromUint64(uint64(value)) + + case PrimitiveStaticTypeWord8: + return NewUnmeteredWord8Value(uint8(value)) + case PrimitiveStaticTypeWord16: + return NewUnmeteredWord16Value(uint16(value)) + case PrimitiveStaticTypeWord32: + return NewUnmeteredWord32Value(uint32(value)) + case PrimitiveStaticTypeWord64: + return NewUnmeteredWord64Value(uint64(value)) + case PrimitiveStaticTypeWord128: + return NewUnmeteredWord128ValueFromUint64(uint64(value)) + case PrimitiveStaticTypeWord256: + return NewUnmeteredWord256ValueFromUint64(uint64(value)) + + default: + panic(errors.NewUnreachableError()) + } +} diff --git a/runtime/interpreter/interpreter.go b/runtime/interpreter/interpreter.go index 7529527f2f..85157677c3 100644 --- a/runtime/interpreter/interpreter.go +++ b/runtime/interpreter/interpreter.go @@ -261,14 +261,10 @@ var _ ast.ExpressionVisitor[Value] = &Interpreter{} // It is reused across all interpreters. var BaseActivation *VariableActivation -var cachedIntegerValues map[StaticType]map[int8]IntegerValue - func init() { // No need to meter since this is only created once BaseActivation = activations.NewActivation[*Variable](nil, nil) defineBaseFunctions(BaseActivation) - - cachedIntegerValues = make(map[StaticType]map[int8]IntegerValue) } func NewInterpreter( @@ -2357,79 +2353,6 @@ func (interpreter *Interpreter) WriteStored( return accountStorage.WriteValue(interpreter, key, value) } -// Get the provided int8 value in the required staticType. -// Note: Assumes that the provided value fits within the constraints of the staticType. -func GetValueForIntegerType(value int8, staticType StaticType) IntegerValue { - typeCache, ok := cachedIntegerValues[staticType] - if !ok { - typeCache = make(map[int8]IntegerValue) - cachedIntegerValues[staticType] = typeCache - } - - val, ok := typeCache[value] - if !ok { - val = getValueForIntegerType(value, staticType) - typeCache[value] = val - } - - return val -} - -// It is important to not meter the memory usage in this function, as it would lead to -// non-determinism as the values produced by this function are cached. -// It could happen that on some execution nodes the value might be cached due to executing a -// transaction or script that needed the value previously, while on other execution nodes it might -// not be cached yet. -func getValueForIntegerType(value int8, staticType StaticType) IntegerValue { - switch staticType { - case PrimitiveStaticTypeInt: - return NewUnmeteredIntValueFromInt64(int64(value)) - case PrimitiveStaticTypeInt8: - return NewUnmeteredInt8Value(value) - case PrimitiveStaticTypeInt16: - return NewUnmeteredInt16Value(int16(value)) - case PrimitiveStaticTypeInt32: - return NewUnmeteredInt32Value(int32(value)) - case PrimitiveStaticTypeInt64: - return NewUnmeteredInt64Value(int64(value)) - case PrimitiveStaticTypeInt128: - return NewUnmeteredInt128ValueFromInt64(int64(value)) - case PrimitiveStaticTypeInt256: - return NewUnmeteredInt256ValueFromInt64(int64(value)) - - case PrimitiveStaticTypeUInt: - return NewUnmeteredUIntValueFromUint64(uint64(value)) - case PrimitiveStaticTypeUInt8: - return NewUnmeteredUInt8Value(uint8(value)) - case PrimitiveStaticTypeUInt16: - return NewUnmeteredUInt16Value(uint16(value)) - case PrimitiveStaticTypeUInt32: - return NewUnmeteredUInt32Value(uint32(value)) - case PrimitiveStaticTypeUInt64: - return NewUnmeteredUInt64Value(uint64(value)) - case PrimitiveStaticTypeUInt128: - return NewUnmeteredUInt128ValueFromUint64(uint64(value)) - case PrimitiveStaticTypeUInt256: - return NewUnmeteredUInt256ValueFromUint64(uint64(value)) - - case PrimitiveStaticTypeWord8: - return NewUnmeteredWord8Value(uint8(value)) - case PrimitiveStaticTypeWord16: - return NewUnmeteredWord16Value(uint16(value)) - case PrimitiveStaticTypeWord32: - return NewUnmeteredWord32Value(uint32(value)) - case PrimitiveStaticTypeWord64: - return NewUnmeteredWord64Value(uint64(value)) - case PrimitiveStaticTypeWord128: - return NewUnmeteredWord128ValueFromUint64(uint64(value)) - case PrimitiveStaticTypeWord256: - return NewUnmeteredWord256ValueFromUint64(uint64(value)) - - default: - panic(errors.NewUnreachableError()) - } -} - type fromStringFunctionValue struct { receiverType sema.Type hostFunction *HostFunctionValue From bceada143f35d7ce007294a8274d11c96c1c5335 Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Thu, 7 Sep 2023 23:32:01 +0530 Subject: [PATCH 070/121] Make InclusiveRange of values.go a pointer type --- encoding/ccf/encode.go | 4 ++-- encoding/json/decode.go | 2 +- encoding/json/encode.go | 4 ++-- runtime/convertValues.go | 12 +++++------ .../imported_values_memory_metering_test.go | 2 +- values.go | 20 +++++++++---------- values_test.go | 2 +- 7 files changed, 23 insertions(+), 23 deletions(-) diff --git a/encoding/ccf/encode.go b/encoding/ccf/encode.go index a2a9600d61..fad8494851 100644 --- a/encoding/ccf/encode.go +++ b/encoding/ccf/encode.go @@ -564,7 +564,7 @@ func (e *Encoder) encodeValue( case cadence.Dictionary: return e.encodeDictionary(v, tids) - case cadence.InclusiveRange: + case *cadence.InclusiveRange: return e.encodeInclusiveRange(v, tids) case cadence.Struct: @@ -984,7 +984,7 @@ func encodeAndSortKeyValuePairs( // encodeInclusiveRange encodes cadence.InclusiveRange as // language=CDDL // inclusiverange-value = [3*3 (key: value, value: value)] -func (e *Encoder) encodeInclusiveRange(v cadence.InclusiveRange, tids ccfTypeIDByCadenceType) error { +func (e *Encoder) encodeInclusiveRange(v *cadence.InclusiveRange, tids ccfTypeIDByCadenceType) error { staticElementType := v.InclusiveRangeType.ElementType // Encode array head with array size of 3. diff --git a/encoding/json/decode.go b/encoding/json/decode.go index 4cbd44d396..46e28e243a 100644 --- a/encoding/json/decode.go +++ b/encoding/json/decode.go @@ -872,7 +872,7 @@ func (d *Decoder) decodeEnum(valueJSON any) cadence.Enum { )) } -func (d *Decoder) decodeInclusiveRange(valueJSON any) cadence.InclusiveRange { +func (d *Decoder) decodeInclusiveRange(valueJSON any) *cadence.InclusiveRange { obj := toObject(valueJSON) start := obj.GetValue(d, startKey) diff --git a/encoding/json/encode.go b/encoding/json/encode.go index 1c9ec52df1..5873d5d83e 100644 --- a/encoding/json/encode.go +++ b/encoding/json/encode.go @@ -343,7 +343,7 @@ func Prepare(v cadence.Value) jsonValue { return prepareArray(v) case cadence.Dictionary: return prepareDictionary(v) - case cadence.InclusiveRange: + case *cadence.InclusiveRange: return prepareInclusiveRange(v) case cadence.Struct: return prepareStruct(v) @@ -606,7 +606,7 @@ func prepareDictionary(v cadence.Dictionary) jsonValue { } } -func prepareInclusiveRange(v cadence.InclusiveRange) jsonValue { +func prepareInclusiveRange(v *cadence.InclusiveRange) jsonValue { return jsonValueObject{ Type: inclusiveRangeTypeStr, Value: jsonInclusiveRangeValue{ diff --git a/runtime/convertValues.go b/runtime/convertValues.go index d6d89ab804..431dc90bdc 100644 --- a/runtime/convertValues.go +++ b/runtime/convertValues.go @@ -630,7 +630,7 @@ func exportCompositeValueAsInclusiveRange( locationRange interpreter.LocationRange, seenReferences seenReferences, ) ( - cadence.InclusiveRange, + *cadence.InclusiveRange, error, ) { compositeValue, ok := v.(*interpreter.CompositeValue) @@ -656,17 +656,17 @@ func exportCompositeValueAsInclusiveRange( startValue, err := getNonComputedField(sema.InclusiveRangeTypeStartFieldName) if err != nil { - return cadence.InclusiveRange{}, err + return &cadence.InclusiveRange{}, err } endValue, err := getNonComputedField(sema.InclusiveRangeTypeEndFieldName) if err != nil { - return cadence.InclusiveRange{}, err + return &cadence.InclusiveRange{}, err } stepValue, err := getNonComputedField(sema.InclusiveRangeTypeStepFieldName) if err != nil { - return cadence.InclusiveRange{}, err + return &cadence.InclusiveRange{}, err } inclusiveRange := cadence.NewMeteredInclusiveRange( @@ -920,7 +920,7 @@ func (i valueImporter) importValue(value cadence.Value, expectedType sema.Type) v.EnumType.Fields, v.Fields, ) - case cadence.InclusiveRange: + case *cadence.InclusiveRange: return i.importInclusiveRangeValue(v, expectedType) case cadence.TypeValue: return i.importTypeValue(v.StaticType) @@ -1452,7 +1452,7 @@ func (i valueImporter) importDictionaryValue( } func (i valueImporter) importInclusiveRangeValue( - v cadence.InclusiveRange, + v *cadence.InclusiveRange, expectedType sema.Type, ) ( *interpreter.CompositeValue, diff --git a/runtime/imported_values_memory_metering_test.go b/runtime/imported_values_memory_metering_test.go index 5587ce5237..9c04ad2398 100644 --- a/runtime/imported_values_memory_metering_test.go +++ b/runtime/imported_values_memory_metering_test.go @@ -407,7 +407,7 @@ func TestImportedValueMemoryMetering(t *testing.T) { `) meter := make(map[common.MemoryKind]uint64) - inclusiveRangeValue := cadence.InclusiveRange{ + inclusiveRangeValue := &cadence.InclusiveRange{ InclusiveRangeType: &cadence.InclusiveRangeType{ ElementType: cadence.IntType{}, }, diff --git a/values.go b/values.go index 969e350f7b..9e6eed9cac 100644 --- a/values.go +++ b/values.go @@ -2061,10 +2061,10 @@ type InclusiveRange struct { fields []Field } -var _ Value = InclusiveRange{} +var _ Value = &InclusiveRange{} -func NewInclusiveRange(start, end, step Value) InclusiveRange { - return InclusiveRange{ +func NewInclusiveRange(start, end, step Value) *InclusiveRange { + return &InclusiveRange{ Start: start, End: end, Step: step, @@ -2074,14 +2074,14 @@ func NewInclusiveRange(start, end, step Value) InclusiveRange { func NewMeteredInclusiveRange( gauge common.MemoryGauge, start, end, step Value, -) InclusiveRange { +) *InclusiveRange { common.UseMemory(gauge, common.CadenceInclusiveRangeValueMemoryUsage) return NewInclusiveRange(start, end, step) } -func (InclusiveRange) isValue() {} +func (*InclusiveRange) isValue() {} -func (v InclusiveRange) Type() Type { +func (v *InclusiveRange) Type() Type { if v.InclusiveRangeType == nil { // Return nil Type instead of Type referencing nil *InclusiveRangeType, // so caller can check if v's type is nil and also prevent nil pointer dereference. @@ -2090,16 +2090,16 @@ func (v InclusiveRange) Type() Type { return v.InclusiveRangeType } -func (v InclusiveRange) MeteredType(common.MemoryGauge) Type { +func (v *InclusiveRange) MeteredType(common.MemoryGauge) Type { return v.Type() } -func (v InclusiveRange) WithType(typ *InclusiveRangeType) InclusiveRange { +func (v *InclusiveRange) WithType(typ *InclusiveRangeType) *InclusiveRange { v.InclusiveRangeType = typ return v } -func (v InclusiveRange) ToGoValue() any { +func (v *InclusiveRange) ToGoValue() any { return []any{ v.Start.ToGoValue(), v.End.ToGoValue(), @@ -2107,7 +2107,7 @@ func (v InclusiveRange) ToGoValue() any { } } -func (v InclusiveRange) String() string { +func (v *InclusiveRange) String() string { if v.InclusiveRangeType == nil { return "" } diff --git a/values_test.go b/values_test.go index 65bd6eb97c..d0e6d4e329 100644 --- a/values_test.go +++ b/values_test.go @@ -224,7 +224,7 @@ func newValueTestCases() map[string]valueTestCase { value: NewInclusiveRange(NewInt(85), NewInt(-85), NewInt(-2)), exampleType: NewInclusiveRangeType(IntType{}), withType: func(value Value, ty Type) Value { - return value.(InclusiveRange).WithType(ty.(*InclusiveRangeType)) + return value.(*InclusiveRange).WithType(ty.(*InclusiveRangeType)) }, string: "InclusiveRange(start: 85, end: -85, step: -2)", }, From 1f96574c06efda3fc6378cc963d54f29da4d9fbc Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Thu, 7 Sep 2023 23:51:07 +0530 Subject: [PATCH 071/121] Handle MemberType as potentially nil in InclusiveRangeType --- runtime/sema/type.go | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/runtime/sema/type.go b/runtime/sema/type.go index 6bbbcbcb18..a9ae634fb7 100644 --- a/runtime/sema/type.go +++ b/runtime/sema/type.go @@ -5259,16 +5259,24 @@ func (r *InclusiveRangeType) String() string { } func (r *InclusiveRangeType) QualifiedString() string { + memberString := "" + if r.MemberType != nil { + memberString = fmt.Sprintf("<%s>", r.MemberType.String()) + } return fmt.Sprintf( "InclusiveRange<%s>", - r.MemberType.QualifiedString(), + memberString, ) } func (r *InclusiveRangeType) ID() TypeID { + memberID := "" + if r.MemberType != nil { + memberID = fmt.Sprintf("<%s>", r.MemberType.ID()) + } return TypeID(fmt.Sprintf( "InclusiveRange<%s>", - r.MemberType.ID(), + memberID, )) } @@ -5313,23 +5321,24 @@ func (*InclusiveRangeType) IsComparable() bool { } func (r *InclusiveRangeType) TypeAnnotationState() TypeAnnotationState { - elementTypeAnnotationState := r.MemberType.TypeAnnotationState() - if elementTypeAnnotationState != TypeAnnotationStateValid { - return elementTypeAnnotationState + if r.MemberType == nil { + return TypeAnnotationStateValid } - return TypeAnnotationStateValid + return r.MemberType.TypeAnnotationState() } func (r *InclusiveRangeType) RewriteWithRestrictedTypes() (Type, bool) { - rewrittenElementType, elementTypeRewritten := r.MemberType.RewriteWithRestrictedTypes() - if elementTypeRewritten { + if r.MemberType == nil { + return r, false + } + rewrittenMemberType, rewritten := r.MemberType.RewriteWithRestrictedTypes() + if rewritten { return &InclusiveRangeType{ - MemberType: rewrittenElementType, + MemberType: rewrittenMemberType, }, true - } else { - return r, false } + return r, false } func (t *InclusiveRangeType) BaseType() Type { From e6eeaed9df4650b49fbdb5bae5b49b1c699ac5a2 Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Sun, 10 Sep 2023 20:09:33 +0530 Subject: [PATCH 072/121] Add test case for usage of InclusiveRange without inner type --- runtime/sema/type.go | 2 +- runtime/tests/checker/range_value_test.go | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/runtime/sema/type.go b/runtime/sema/type.go index a9ae634fb7..20ed60e754 100644 --- a/runtime/sema/type.go +++ b/runtime/sema/type.go @@ -5297,7 +5297,7 @@ func (r *InclusiveRangeType) IsResourceType() bool { } func (r *InclusiveRangeType) IsInvalidType() bool { - return r.MemberType.IsInvalidType() + return r.MemberType != nil && r.MemberType.IsInvalidType() } func (r *InclusiveRangeType) IsStorable(results map[*Member]bool) bool { diff --git a/runtime/tests/checker/range_value_test.go b/runtime/tests/checker/range_value_test.go index 964bd001e6..1e409e1c41 100644 --- a/runtime/tests/checker/range_value_test.go +++ b/runtime/tests/checker/range_value_test.go @@ -362,4 +362,11 @@ func TestCheckInclusiveRangeConstructionInvalid(t *testing.T) { []error{&sema.IncorrectArgumentLabelError{}}, ) } + + runInvalidCase( + t, + "without_inner_type_annotation", + "let r: InclusiveRange<> = InclusiveRange(1, 10)", + []error{&sema.InvalidTypeArgumentCountError{}}, + ) } From 5f2ddd8165bb38254b50b7025a489563451e7069 Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Sun, 10 Sep 2023 20:32:39 +0530 Subject: [PATCH 073/121] Add a mutex in range_value_test.go to protect concurrent access to the cachedIntegerValues map --- runtime/tests/interpreter/range_value_test.go | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/runtime/tests/interpreter/range_value_test.go b/runtime/tests/interpreter/range_value_test.go index e6ce301f1f..fad5f5d1ac 100644 --- a/runtime/tests/interpreter/range_value_test.go +++ b/runtime/tests/interpreter/range_value_test.go @@ -21,6 +21,7 @@ package interpreter_test import ( "fmt" "strings" + "sync" "testing" "github.com/stretchr/testify/require" @@ -33,6 +34,14 @@ import ( . "github.com/onflow/cadence/runtime/tests/utils" ) +var getValueForIntegerTypeMutex sync.Mutex + +func synchronizedGetValueForIntegerType(value int8, staticType interpreter.StaticType) interpreter.IntegerValue { + getValueForIntegerTypeMutex.Lock() + defer getValueForIntegerTypeMutex.Unlock() + return interpreter.GetValueForIntegerType(value, staticType) +} + type containsTestCase struct { param int64 expectedWithoutStep bool @@ -417,9 +426,9 @@ func TestInclusiveRange(t *testing.T) { expectedRangeValue = interpreter.NewInclusiveRangeValueWithStep( inter, interpreter.EmptyLocationRange, - interpreter.GetValueForIntegerType(testCase.s, elementType), - interpreter.GetValueForIntegerType(testCase.e, elementType), - interpreter.GetValueForIntegerType(testCase.step, elementType), + synchronizedGetValueForIntegerType(testCase.s, elementType), + synchronizedGetValueForIntegerType(testCase.e, elementType), + synchronizedGetValueForIntegerType(testCase.step, elementType), rangeType, rangeSemaType, ) @@ -427,8 +436,8 @@ func TestInclusiveRange(t *testing.T) { expectedRangeValue = interpreter.NewInclusiveRangeValue( inter, interpreter.EmptyLocationRange, - interpreter.GetValueForIntegerType(testCase.s, elementType), - interpreter.GetValueForIntegerType(testCase.e, elementType), + synchronizedGetValueForIntegerType(testCase.s, elementType), + synchronizedGetValueForIntegerType(testCase.e, elementType), rangeType, rangeSemaType, ) @@ -482,7 +491,7 @@ func TestGetValueForIntegerType(t *testing.T) { staticType := interpreter.ConvertSemaToStaticType(nil, integerType) // Panics if not handled. - _ = interpreter.GetValueForIntegerType(int8(1), staticType) + _ = synchronizedGetValueForIntegerType(int8(1), staticType) } } From d97eef694f0600072648e89f35362a1a5039c12e Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Fri, 22 Sep 2023 22:54:35 +0530 Subject: [PATCH 074/121] Add checkParameterizedTypeIsInstantiated --- runtime/sema/checker.go | 44 +++++++++++++++++++++++ runtime/sema/errors.go | 18 ++++++++++ runtime/sema/type.go | 3 -- runtime/tests/checker/range_value_test.go | 9 ++++- 4 files changed, 70 insertions(+), 4 deletions(-) diff --git a/runtime/sema/checker.go b/runtime/sema/checker.go index 8706de2f69..fbe9759092 100644 --- a/runtime/sema/checker.go +++ b/runtime/sema/checker.go @@ -2048,6 +2048,7 @@ func (checker *Checker) checkTypeAnnotation(typeAnnotation TypeAnnotation, pos a } checker.checkInvalidInterfaceAsType(typeAnnotation.Type, pos) + checker.checkParameterizedTypeIsInstantiated(typeAnnotation.Type, pos) } func (checker *Checker) checkInvalidInterfaceAsType(ty Type, pos ast.HasPosition) { @@ -2067,6 +2068,49 @@ func (checker *Checker) checkInvalidInterfaceAsType(ty Type, pos ast.HasPosition } } +func (checker *Checker) checkParameterizedTypeIsInstantiated(ty Type, pos ast.HasPosition) { + parameterizedType, ok := ty.(ParameterizedType) + if !ok { + return + } + + typeArgs := parameterizedType.TypeArguments() + typeParameters := parameterizedType.TypeParameters() + + typeArgumentCount := len(typeArgs) + typeParameterCount := len(typeParameters) + + if typeArgumentCount != typeParameterCount { + checker.report( + &InvalidTypeArgumentCountError{ + TypeParameterCount: typeParameterCount, + TypeArgumentCount: typeArgumentCount, + Range: ast.NewRange( + checker.memoryGauge, + pos.StartPosition(), + pos.EndPosition(checker.memoryGauge), + ), + }, + ) + } + + // Ensure that each non-optional typeparameter is non-nil. + for index, typeParam := range typeParameters { + if !typeParam.Optional && typeArgs[index] == nil { + checker.report( + &MissingTypeArgumentError{ + TypeArgumentName: typeParam.Name, + Range: ast.NewRange( + checker.memoryGauge, + pos.StartPosition(), + pos.EndPosition(checker.memoryGauge), + ), + }, + ) + } + } +} + func (checker *Checker) ValueActivationDepth() int { return checker.valueActivations.Depth() } diff --git a/runtime/sema/errors.go b/runtime/sema/errors.go index 958ab6009f..849a0eaf41 100644 --- a/runtime/sema/errors.go +++ b/runtime/sema/errors.go @@ -3700,6 +3700,24 @@ func (e *InvalidTypeArgumentCountError) SecondaryError() string { ) } +// MissingTypeArgumentError + +type MissingTypeArgumentError struct { + TypeArgumentName string + ast.Range +} + +var _ SemanticError = &MissingTypeArgumentError{} +var _ errors.UserError = &MissingTypeArgumentError{} + +func (e *MissingTypeArgumentError) isSemanticError() {} + +func (*MissingTypeArgumentError) IsUserError() {} + +func (e *MissingTypeArgumentError) Error() string { + return fmt.Sprintf("non-optional type argument %s missing", e.TypeArgumentName) +} + // TypeParameterTypeInferenceError type TypeParameterTypeInferenceError struct { diff --git a/runtime/sema/type.go b/runtime/sema/type.go index 20ed60e754..4dbb326c0d 100644 --- a/runtime/sema/type.go +++ b/runtime/sema/type.go @@ -5357,9 +5357,6 @@ func (t *InclusiveRangeType) Instantiate(typeArguments []Type, report func(err e func (t *InclusiveRangeType) TypeArguments() []Type { memberType := t.MemberType - if memberType == nil { - memberType = IntegerType - } return []Type{ memberType, } diff --git a/runtime/tests/checker/range_value_test.go b/runtime/tests/checker/range_value_test.go index 1e409e1c41..28c47be275 100644 --- a/runtime/tests/checker/range_value_test.go +++ b/runtime/tests/checker/range_value_test.go @@ -367,6 +367,13 @@ func TestCheckInclusiveRangeConstructionInvalid(t *testing.T) { t, "without_inner_type_annotation", "let r: InclusiveRange<> = InclusiveRange(1, 10)", - []error{&sema.InvalidTypeArgumentCountError{}}, + []error{&sema.InvalidTypeArgumentCountError{}, &sema.MissingTypeArgumentError{}}, + ) + + runInvalidCase( + t, + "without_inner_type_annotation", + "let r: InclusiveRange = InclusiveRange(1, 10)", + []error{&sema.MissingTypeArgumentError{}}, ) } From 7904b95448ad2b9130708c9a1673a8bcbd98aac4 Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Fri, 22 Sep 2023 23:16:03 +0530 Subject: [PATCH 075/121] Use IsSameTypeKind --- runtime/interpreter/interpreter.go | 2 +- runtime/tests/checker/range_value_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/runtime/interpreter/interpreter.go b/runtime/interpreter/interpreter.go index 85157677c3..01ba2d086c 100644 --- a/runtime/interpreter/interpreter.go +++ b/runtime/interpreter/interpreter.go @@ -3566,7 +3566,7 @@ var runtimeTypeConstructors = []runtimeTypeConstructor{ ty := typeValue.Type // InclusiveRanges must hold integers elemSemaTy := inter.MustConvertStaticToSemaType(ty) - if !elemSemaTy.Tag().BelongsTo(sema.IntegerTypeTag) { + if !sema.IsSameTypeKind(elemSemaTy, sema.IntegerType) { return Nil } diff --git a/runtime/tests/checker/range_value_test.go b/runtime/tests/checker/range_value_test.go index 28c47be275..196f3baf25 100644 --- a/runtime/tests/checker/range_value_test.go +++ b/runtime/tests/checker/range_value_test.go @@ -372,7 +372,7 @@ func TestCheckInclusiveRangeConstructionInvalid(t *testing.T) { runInvalidCase( t, - "without_inner_type_annotation", + "without instantiation type", "let r: InclusiveRange = InclusiveRange(1, 10)", []error{&sema.MissingTypeArgumentError{}}, ) From 8732e3dee2bfed7a602f14048acfadc9137b8664 Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Sun, 24 Sep 2023 21:01:11 +0530 Subject: [PATCH 076/121] Add test case in TestCheckTypeArguments --- runtime/tests/checker/typeargument_test.go | 24 ++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/runtime/tests/checker/typeargument_test.go b/runtime/tests/checker/typeargument_test.go index 0149708171..094128f07e 100644 --- a/runtime/tests/checker/typeargument_test.go +++ b/runtime/tests/checker/typeargument_test.go @@ -25,6 +25,7 @@ import ( "github.com/stretchr/testify/require" "github.com/onflow/cadence/runtime/sema" + "github.com/onflow/cadence/runtime/stdlib" ) func TestCheckTypeArguments(t *testing.T) { @@ -53,6 +54,29 @@ func TestCheckTypeArguments(t *testing.T) { require.Nil(t, capType.(*sema.CapabilityType).BorrowType) }) + t.Run("inclusive range, no instantiation", func(t *testing.T) { + + t.Parallel() + + baseValueActivation := sema.NewVariableActivation(sema.BaseValueActivation) + baseValueActivation.DeclareValue(stdlib.InclusiveRangeConstructorFunction) + + _, err := ParseAndCheckWithOptions(t, + ` + let inclusiveRange: InclusiveRange = InclusiveRange(1, 10) + `, + ParseAndCheckOptions{ + Config: &sema.Config{ + BaseValueActivation: baseValueActivation, + }, + }, + ) + + errs := RequireCheckerErrors(t, err, 1) + + assert.IsType(t, &sema.MissingTypeArgumentError{}, errs[0]) + }) + t.Run("capability, instantiation with no arguments", func(t *testing.T) { t.Parallel() From 81aed0fec6d151e1cbec9fe0bac1ee077a31a53c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Tue, 3 Oct 2023 13:53:04 -0700 Subject: [PATCH 077/121] fix qualified string and type ID generation --- runtime/interpreter/statictype.go | 6 +- runtime/sema/type.go | 117 ++++++++++++++++-------------- 2 files changed, 67 insertions(+), 56 deletions(-) diff --git a/runtime/interpreter/statictype.go b/runtime/interpreter/statictype.go index fd461ed0bf..d81e2d24b2 100644 --- a/runtime/interpreter/statictype.go +++ b/runtime/interpreter/statictype.go @@ -318,7 +318,7 @@ func (InclusiveRangeStaticType) elementSize() uint { } func (t InclusiveRangeStaticType) String() string { - return fmt.Sprintf("InclusiveRange<%s>", t.ElementType) + return t.MeteredString(nil) } func (t InclusiveRangeStaticType) MeteredString(memoryGauge common.MemoryGauge) string { @@ -338,6 +338,10 @@ func (t InclusiveRangeStaticType) Equal(other StaticType) bool { return t.ElementType.Equal(otherRangeType.ElementType) } +func (t InclusiveRangeStaticType) ID() TypeID { + return sema.InclusiveRangeTypeID(string(t.ElementType.ID())) +} + // ConstantSizedStaticType type ConstantSizedStaticType struct { diff --git a/runtime/sema/type.go b/runtime/sema/type.go index 2e7951db12..e133aa6442 100644 --- a/runtime/sema/type.go +++ b/runtime/sema/type.go @@ -5291,10 +5291,10 @@ func (*InclusiveRangeType) Tag() TypeTag { return InclusiveRangeTypeTag } -func (r *InclusiveRangeType) String() string { +func (t *InclusiveRangeType) String() string { memberString := "" - if r.MemberType != nil { - memberString = fmt.Sprintf("<%s>", r.MemberType.String()) + if t.MemberType != nil { + memberString = fmt.Sprintf("<%s>", t.MemberType.String()) } return fmt.Sprintf( "InclusiveRange%s", @@ -5302,87 +5302,94 @@ func (r *InclusiveRangeType) String() string { ) } -func (r *InclusiveRangeType) QualifiedString() string { +func (t *InclusiveRangeType) QualifiedString() string { memberString := "" - if r.MemberType != nil { - memberString = fmt.Sprintf("<%s>", r.MemberType.String()) + if t.MemberType != nil { + memberString = fmt.Sprintf("<%s>", t.MemberType.QualifiedString()) } return fmt.Sprintf( - "InclusiveRange<%s>", + "InclusiveRange%s", memberString, ) } -func (r *InclusiveRangeType) ID() TypeID { - memberID := "" - if r.MemberType != nil { - memberID = fmt.Sprintf("<%s>", r.MemberType.ID()) +func InclusiveRangeTypeID(memberTypeID string) TypeID { + if memberTypeID != "" { + memberTypeID = fmt.Sprintf("<%s>", memberTypeID) } return TypeID(fmt.Sprintf( - "InclusiveRange<%s>", - memberID, + "InclusiveRange%s", + memberTypeID, )) } -func (r *InclusiveRangeType) Equal(other Type) bool { +func (t *InclusiveRangeType) ID() TypeID { + var memberTypeID string + if t.MemberType != nil { + memberTypeID = string(t.MemberType.ID()) + } + return InclusiveRangeTypeID(memberTypeID) +} + +func (t *InclusiveRangeType) Equal(other Type) bool { otherRange, ok := other.(*InclusiveRangeType) if !ok { return false } if otherRange.MemberType == nil { - return r.MemberType == nil + return t.MemberType == nil } - return otherRange.MemberType.Equal(r.MemberType) + return otherRange.MemberType.Equal(t.MemberType) } -func (r *InclusiveRangeType) IsResourceType() bool { +func (t *InclusiveRangeType) IsResourceType() bool { return false } -func (r *InclusiveRangeType) IsInvalidType() bool { - return r.MemberType != nil && r.MemberType.IsInvalidType() +func (t *InclusiveRangeType) IsInvalidType() bool { + return t.MemberType != nil && t.MemberType.IsInvalidType() } -func (r *InclusiveRangeType) IsStorable(results map[*Member]bool) bool { - return r.MemberType.IsStorable(results) +func (t *InclusiveRangeType) IsStorable(results map[*Member]bool) bool { + return t.MemberType.IsStorable(results) } -func (r *InclusiveRangeType) IsExportable(results map[*Member]bool) bool { - return r.MemberType.IsExportable(results) +func (t *InclusiveRangeType) IsExportable(results map[*Member]bool) bool { + return t.MemberType.IsExportable(results) } -func (r *InclusiveRangeType) IsImportable(results map[*Member]bool) bool { - return r.MemberType.IsImportable(results) +func (t *InclusiveRangeType) IsImportable(results map[*Member]bool) bool { + return t.MemberType.IsImportable(results) } -func (r *InclusiveRangeType) IsEquatable() bool { - return r.MemberType.IsEquatable() +func (t *InclusiveRangeType) IsEquatable() bool { + return t.MemberType.IsEquatable() } func (*InclusiveRangeType) IsComparable() bool { return false } -func (r *InclusiveRangeType) TypeAnnotationState() TypeAnnotationState { - if r.MemberType == nil { +func (t *InclusiveRangeType) TypeAnnotationState() TypeAnnotationState { + if t.MemberType == nil { return TypeAnnotationStateValid } - return r.MemberType.TypeAnnotationState() + return t.MemberType.TypeAnnotationState() } -func (r *InclusiveRangeType) RewriteWithRestrictedTypes() (Type, bool) { - if r.MemberType == nil { - return r, false +func (t *InclusiveRangeType) RewriteWithRestrictedTypes() (Type, bool) { + if t.MemberType == nil { + return t, false } - rewrittenMemberType, rewritten := r.MemberType.RewriteWithRestrictedTypes() + rewrittenMemberType, rewritten := t.MemberType.RewriteWithRestrictedTypes() if rewritten { return &InclusiveRangeType{ MemberType: rewrittenMemberType, }, true } - return r, false + return t, false } func (t *InclusiveRangeType) BaseType() Type { @@ -5443,9 +5450,9 @@ const inclusiveRangeTypeContainsFunctionDocString = ` Returns true if the given integer is in the InclusiveRange sequence ` -func (r *InclusiveRangeType) GetMembers() map[string]MemberResolver { - r.initializeMemberResolvers() - return r.memberResolvers +func (t *InclusiveRangeType) GetMembers() map[string]MemberResolver { + t.initializeMemberResolvers() + return t.memberResolvers } func InclusiveRangeContainsFunctionType(elementType Type) *FunctionType { @@ -5463,17 +5470,17 @@ func InclusiveRangeContainsFunctionType(elementType Type) *FunctionType { } } -func (r *InclusiveRangeType) initializeMemberResolvers() { - r.memberResolversOnce.Do(func() { - r.memberResolvers = withBuiltinMembers(r, map[string]MemberResolver{ +func (t *InclusiveRangeType) initializeMemberResolvers() { + t.memberResolversOnce.Do(func() { + t.memberResolvers = withBuiltinMembers(t, map[string]MemberResolver{ InclusiveRangeTypeStartFieldName: { Kind: common.DeclarationKindField, Resolve: func(memoryGauge common.MemoryGauge, identifier string, _ ast.Range, _ func(error)) *Member { return NewPublicConstantFieldMember( memoryGauge, - r, + t, identifier, - r.MemberType, + t.MemberType, inclusiveRangeTypeStartFieldDocString, ) }, @@ -5483,9 +5490,9 @@ func (r *InclusiveRangeType) initializeMemberResolvers() { Resolve: func(memoryGauge common.MemoryGauge, identifier string, _ ast.Range, _ func(error)) *Member { return NewPublicConstantFieldMember( memoryGauge, - r, + t, identifier, - r.MemberType, + t.MemberType, inclusiveRangeTypeEndFieldDocString, ) }, @@ -5495,9 +5502,9 @@ func (r *InclusiveRangeType) initializeMemberResolvers() { Resolve: func(memoryGauge common.MemoryGauge, identifier string, _ ast.Range, _ func(error)) *Member { return NewPublicConstantFieldMember( memoryGauge, - r, + t, identifier, - r.MemberType, + t.MemberType, inclusiveRangeTypeStepFieldDocString, ) }, @@ -5505,11 +5512,11 @@ func (r *InclusiveRangeType) initializeMemberResolvers() { InclusiveRangeTypeContainsFunctionName: { Kind: common.DeclarationKindFunction, Resolve: func(memoryGauge common.MemoryGauge, identifier string, targetRange ast.Range, report func(error)) *Member { - elementType := r.MemberType + elementType := t.MemberType return NewPublicFunctionMember( memoryGauge, - r, + t, identifier, InclusiveRangeContainsFunctionType(elementType), inclusiveRangeTypeContainsFunctionDocString, @@ -5520,15 +5527,15 @@ func (r *InclusiveRangeType) initializeMemberResolvers() { }) } -func (r *InclusiveRangeType) ElementType(_ bool) Type { - return r.MemberType +func (t *InclusiveRangeType) ElementType(_ bool) Type { + return t.MemberType } func (*InclusiveRangeType) AllowsValueIndexingAssignment() bool { return false } -func (r *InclusiveRangeType) Unify( +func (t *InclusiveRangeType) Unify( other Type, typeParameters *TypeParameterTypeOrderedMap, report func(err error), @@ -5539,11 +5546,11 @@ func (r *InclusiveRangeType) Unify( return false } - return r.MemberType.Unify(otherRange.MemberType, typeParameters, report, outerRange) + return t.MemberType.Unify(otherRange.MemberType, typeParameters, report, outerRange) } -func (r *InclusiveRangeType) Resolve(typeArguments *TypeParameterTypeOrderedMap) Type { - memberType := r.MemberType.Resolve(typeArguments) +func (t *InclusiveRangeType) Resolve(typeArguments *TypeParameterTypeOrderedMap) Type { + memberType := t.MemberType.Resolve(typeArguments) if memberType == nil { return nil } From 1af52af7eb1468c0f40bcdc2a5f00e11af5aa24a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Tue, 3 Oct 2023 14:31:21 -0700 Subject: [PATCH 078/121] improve small integer cache --- runtime/interpreter/integer.go | 52 ++++++++++++------- runtime/interpreter/value_range.go | 6 +-- runtime/tests/interpreter/range_value_test.go | 32 +++++------- 3 files changed, 50 insertions(+), 40 deletions(-) diff --git a/runtime/interpreter/integer.go b/runtime/interpreter/integer.go index 62bfa3e372..3b32a694ad 100644 --- a/runtime/interpreter/integer.go +++ b/runtime/interpreter/integer.go @@ -18,38 +18,52 @@ package interpreter -import "github.com/onflow/cadence/runtime/errors" +import ( + "sync" -var cachedIntegerValues map[StaticType]map[int8]IntegerValue + "github.com/onflow/cadence/runtime/errors" +) -func init() { - cachedIntegerValues = make(map[StaticType]map[int8]IntegerValue) +func GetSmallIntegerValue(value int8, staticType StaticType) IntegerValue { + return cachedSmallIntegerValues.Get(value, staticType) } -// Get the provided int8 value in the required staticType. -// Note: Assumes that the provided value fits within the constraints of the staticType. -func GetValueForIntegerType(value int8, staticType StaticType) IntegerValue { - typeCache, ok := cachedIntegerValues[staticType] - if !ok { - typeCache = make(map[int8]IntegerValue) - cachedIntegerValues[staticType] = typeCache +type integerValueCacheKey struct { + value int8 + staticType StaticType +} + +type smallIntegerValueCache struct { + m sync.Map +} + +var cachedSmallIntegerValues = smallIntegerValueCache{} + +func (c *smallIntegerValueCache) Get(value int8, staticType StaticType) IntegerValue { + key := integerValueCacheKey{ + value: value, + staticType: staticType, } - val, ok := typeCache[value] - if !ok { - val = getValueForIntegerType(value, staticType) - typeCache[value] = val + existingValue, ok := c.m.Load(key) + if ok { + return existingValue.(IntegerValue) } - return val + newValue := c.new(value, staticType) + c.m.Store(key, newValue) + return newValue } -// It is important to not meter the memory usage in this function, as it would lead to -// non-determinism as the values produced by this function are cached. +// getValueForIntegerType returns a Cadence integer value +// of the given Cadence static type for the given Go integer value. +// +// It is important NOT to meter the memory usage in this function, +// as it would lead to non-determinism as the values produced by this function are cached. // It could happen that on some execution nodes the value might be cached due to executing a // transaction or script that needed the value previously, while on other execution nodes it might // not be cached yet. -func getValueForIntegerType(value int8, staticType StaticType) IntegerValue { +func (c *smallIntegerValueCache) new(value int8, staticType StaticType) IntegerValue { switch staticType { case PrimitiveStaticTypeInt: return NewUnmeteredIntValueFromInt64(int64(value)) diff --git a/runtime/interpreter/value_range.go b/runtime/interpreter/value_range.go index 56f6965a52..835caac068 100644 --- a/runtime/interpreter/value_range.go +++ b/runtime/interpreter/value_range.go @@ -42,7 +42,7 @@ func NewInclusiveRangeValue( panic(errors.NewUnreachableError()) } - step := GetValueForIntegerType(1, rangeStaticType.ElementType) + step := GetSmallIntegerValue(1, rangeStaticType.ElementType) if startComparable.Greater(interpreter, endComparable, locationRange) { elemSemaTy := interpreter.MustConvertStaticToSemaType(rangeStaticType.ElementType) if elemSemaTy.Tag().BelongsTo(sema.UnsignedIntegerTypeTag) { @@ -86,7 +86,7 @@ func NewInclusiveRangeValueWithStep( rangeSemaType *sema.InclusiveRangeType, ) *CompositeValue { - zeroValue := GetValueForIntegerType(0, start.StaticType(interpreter)) + zeroValue := GetSmallIntegerValue(0, start.StaticType(interpreter)) // Validate that the step is non-zero. if step.Equal(interpreter, locationRange, zeroValue) { @@ -211,7 +211,7 @@ func rangeContains( panic(errors.NewUnreachableError()) } - zeroValue := GetValueForIntegerType(0, rangeType.ElementType) + zeroValue := GetSmallIntegerValue(0, rangeType.ElementType) mod := diff.Mod(interpreter, step, locationRange) result = mod.Equal(interpreter, locationRange, zeroValue) } diff --git a/runtime/tests/interpreter/range_value_test.go b/runtime/tests/interpreter/range_value_test.go index fad5f5d1ac..f50084e4fc 100644 --- a/runtime/tests/interpreter/range_value_test.go +++ b/runtime/tests/interpreter/range_value_test.go @@ -21,7 +21,6 @@ package interpreter_test import ( "fmt" "strings" - "sync" "testing" "github.com/stretchr/testify/require" @@ -34,14 +33,6 @@ import ( . "github.com/onflow/cadence/runtime/tests/utils" ) -var getValueForIntegerTypeMutex sync.Mutex - -func synchronizedGetValueForIntegerType(value int8, staticType interpreter.StaticType) interpreter.IntegerValue { - getValueForIntegerTypeMutex.Lock() - defer getValueForIntegerTypeMutex.Unlock() - return interpreter.GetValueForIntegerType(value, staticType) -} - type containsTestCase struct { param int64 expectedWithoutStep bool @@ -413,11 +404,12 @@ func TestInclusiveRange(t *testing.T) { require.NoError(t, err) - elementType := interpreter.ConvertSemaToStaticType( + integerType := interpreter.ConvertSemaToStaticType( nil, testCase.ty, ) - rangeType := interpreter.NewInclusiveRangeStaticType(nil, elementType) + + rangeType := interpreter.NewInclusiveRangeStaticType(nil, integerType) rangeSemaType := sema.NewInclusiveRangeType(nil, testCase.ty) var expectedRangeValue *interpreter.CompositeValue @@ -426,9 +418,9 @@ func TestInclusiveRange(t *testing.T) { expectedRangeValue = interpreter.NewInclusiveRangeValueWithStep( inter, interpreter.EmptyLocationRange, - synchronizedGetValueForIntegerType(testCase.s, elementType), - synchronizedGetValueForIntegerType(testCase.e, elementType), - synchronizedGetValueForIntegerType(testCase.step, elementType), + interpreter.GetSmallIntegerValue(testCase.s, integerType), + interpreter.GetSmallIntegerValue(testCase.e, integerType), + interpreter.GetSmallIntegerValue(testCase.step, integerType), rangeType, rangeSemaType, ) @@ -436,8 +428,8 @@ func TestInclusiveRange(t *testing.T) { expectedRangeValue = interpreter.NewInclusiveRangeValue( inter, interpreter.EmptyLocationRange, - synchronizedGetValueForIntegerType(testCase.s, elementType), - synchronizedGetValueForIntegerType(testCase.e, elementType), + interpreter.GetSmallIntegerValue(testCase.s, integerType), + interpreter.GetSmallIntegerValue(testCase.e, integerType), rangeType, rangeSemaType, ) @@ -488,10 +480,14 @@ func TestGetValueForIntegerType(t *testing.T) { continue } - staticType := interpreter.ConvertSemaToStaticType(nil, integerType) + integerStaticType := interpreter.ConvertSemaToStaticType(nil, integerType) + + var primitiveIntegerType interpreter.PrimitiveStaticType + require.IsType(t, interpreter.PrimitiveStaticTypeUnknown, integerStaticType) + primitiveIntegerType = integerStaticType.(interpreter.PrimitiveStaticType) // Panics if not handled. - _ = synchronizedGetValueForIntegerType(int8(1), staticType) + _ = interpreter.GetSmallIntegerValue(int8(1), primitiveIntegerType) } } From 56c8ac758b5bf41c4dfa5222e859e3bf8d29f341 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Tue, 3 Oct 2023 14:33:30 -0700 Subject: [PATCH 079/121] remove unnecessary assertion --- runtime/tests/interpreter/range_value_test.go | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/runtime/tests/interpreter/range_value_test.go b/runtime/tests/interpreter/range_value_test.go index f50084e4fc..de1ba55c32 100644 --- a/runtime/tests/interpreter/range_value_test.go +++ b/runtime/tests/interpreter/range_value_test.go @@ -482,12 +482,8 @@ func TestGetValueForIntegerType(t *testing.T) { integerStaticType := interpreter.ConvertSemaToStaticType(nil, integerType) - var primitiveIntegerType interpreter.PrimitiveStaticType - require.IsType(t, interpreter.PrimitiveStaticTypeUnknown, integerStaticType) - primitiveIntegerType = integerStaticType.(interpreter.PrimitiveStaticType) - // Panics if not handled. - _ = interpreter.GetSmallIntegerValue(int8(1), primitiveIntegerType) + _ = interpreter.GetSmallIntegerValue(int8(1), integerStaticType) } } From 9315a888a8ef55d55868b34457c57a623d024078 Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Wed, 4 Oct 2023 21:49:46 +0530 Subject: [PATCH 080/121] Separate functions for static type conformance check --- runtime/interpreter/value.go | 148 ++++++++++++++++++++--------------- 1 file changed, 84 insertions(+), 64 deletions(-) diff --git a/runtime/interpreter/value.go b/runtime/interpreter/value.go index 66f40e163f..1fd6503f34 100644 --- a/runtime/interpreter/value.go +++ b/runtime/interpreter/value.go @@ -17081,97 +17081,117 @@ func (v *CompositeValue) ConformsToStaticType( switch staticType.(type) { case CompositeStaticType: - compositeType, ok := semaType.(*sema.CompositeType) - if !ok || - v.Kind != compositeType.Kind || - v.TypeID() != compositeType.ID() { + return v.CompositeStaticTypeConformsToStaticType(interpreter, locationRange, results, semaType) - return false - } + // CompositeValue is also used for storing types which aren't CompositeStaticType. + // E.g. InclusiveRange. + case InclusiveRangeStaticType: + return v.InclusiveRangeStaticTypeConformsToStaticType(interpreter, locationRange, results, semaType) - if compositeType.Kind == common.CompositeKindAttachment { - base := v.getBaseValue().Value - if base == nil || !base.ConformsToStaticType(interpreter, locationRange, results) { - return false - } - } + default: + return false + } +} - fieldsLen := int(v.dictionary.Count()) - if v.ComputedFields != nil { - fieldsLen += len(v.ComputedFields) - } +func (v *CompositeValue) CompositeStaticTypeConformsToStaticType( + interpreter *Interpreter, + locationRange LocationRange, + results TypeConformanceResults, + semaType sema.Type, +) bool { + compositeType, ok := semaType.(*sema.CompositeType) + if !ok || + v.Kind != compositeType.Kind || + v.TypeID() != compositeType.ID() { - if fieldsLen != len(compositeType.Fields) { + return false + } + + if compositeType.Kind == common.CompositeKindAttachment { + base := v.getBaseValue().Value + if base == nil || !base.ConformsToStaticType(interpreter, locationRange, results) { return false } + } - for _, fieldName := range compositeType.Fields { - value := v.GetField(interpreter, locationRange, fieldName) - if value == nil { - if v.ComputedFields == nil { - return false - } + fieldsLen := int(v.dictionary.Count()) + if v.ComputedFields != nil { + fieldsLen += len(v.ComputedFields) + } - fieldGetter, ok := v.ComputedFields[fieldName] - if !ok { - return false - } + if fieldsLen != len(compositeType.Fields) { + return false + } - value = fieldGetter(interpreter, locationRange) + for _, fieldName := range compositeType.Fields { + value := v.GetField(interpreter, locationRange, fieldName) + if value == nil { + if v.ComputedFields == nil { + return false } - member, ok := compositeType.Members.Get(fieldName) + fieldGetter, ok := v.ComputedFields[fieldName] if !ok { return false } - fieldStaticType := value.StaticType(interpreter) + value = fieldGetter(interpreter, locationRange) + } - if !interpreter.IsSubTypeOfSemaType(fieldStaticType, member.TypeAnnotation.Type) { - return false - } + member, ok := compositeType.Members.Get(fieldName) + if !ok { + return false + } - if !value.ConformsToStaticType( - interpreter, - locationRange, - results, - ) { - return false - } + fieldStaticType := value.StaticType(interpreter) + + if !interpreter.IsSubTypeOfSemaType(fieldStaticType, member.TypeAnnotation.Type) { + return false } - // CompositeValue is also used for storing types which aren't CompositeStaticType. - // E.g. InclusiveRange. - case InclusiveRangeStaticType: - inclusiveRangeType, ok := semaType.(*sema.InclusiveRangeType) - if !ok { + if !value.ConformsToStaticType( + interpreter, + locationRange, + results, + ) { return false } + } - expectedMemberStaticType := ConvertSemaToStaticType(interpreter, inclusiveRangeType.MemberType) - for _, fieldName := range sema.InclusiveRangeTypeFieldNames { - value := v.GetField(interpreter, locationRange, fieldName) + return true +} + +func (v *CompositeValue) InclusiveRangeStaticTypeConformsToStaticType( + interpreter *Interpreter, + locationRange LocationRange, + results TypeConformanceResults, + semaType sema.Type, +) bool { + inclusiveRangeType, ok := semaType.(*sema.InclusiveRangeType) + if !ok { + return false + } - fieldStaticType := value.StaticType(interpreter) + expectedMemberStaticType := ConvertSemaToStaticType(interpreter, inclusiveRangeType.MemberType) + for _, fieldName := range sema.InclusiveRangeTypeFieldNames { + value := v.GetField(interpreter, locationRange, fieldName) - // InclusiveRange is non-covariant. - // For e.g. we disallow assigning InclusiveRange to an InclusiveRange. - // Hence we do an exact equality check instead of a sub-type check. - if !fieldStaticType.Equal(expectedMemberStaticType) { - return false - } + fieldStaticType := value.StaticType(interpreter) - if !value.ConformsToStaticType( - interpreter, - locationRange, - results, - ) { - return false - } + // InclusiveRange is non-covariant. + // For e.g. we disallow assigning InclusiveRange to an InclusiveRange. + // Hence we do an exact equality check instead of a sub-type check. + if !fieldStaticType.Equal(expectedMemberStaticType) { + return false } - default: - return false + if !value.ConformsToStaticType( + interpreter, + locationRange, + results, + ) { + return false + } } return true From dd77fb755cb101d46462a4d1b655013aa10daf3c Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Wed, 4 Oct 2023 23:18:40 +0530 Subject: [PATCH 081/121] Add runtime test to save and load InclusiveRange in two transactions --- runtime/runtime_test.go | 87 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/runtime/runtime_test.go b/runtime/runtime_test.go index 8f1af02fb3..061c3e10ed 100644 --- a/runtime/runtime_test.go +++ b/runtime/runtime_test.go @@ -2365,6 +2365,93 @@ func TestRuntimeCompositeFunctionInvocationFromImportingProgram(t *testing.T) { require.NoError(t, err) } +// TestRuntimeStorageMultipleTransactionsInclusiveRangeFunction tests a function call +// of a stored inclusive range declared in an imported program +func TestRuntimeStorageMultipleTransactionsInclusiveRangeFunction(t *testing.T) { + + t.Parallel() + + runtime := newTestInterpreterRuntime() + + inclusiveRangeCreation := []byte(` + pub fun createInclusiveRange(): InclusiveRange { + return InclusiveRange(10, 20) + } + `) + + script1 := []byte(` + import "inclusive-range-creation" + + transaction { + + prepare(signer: AuthAccount) { + let ir = createInclusiveRange() + signer.save(ir, to: /storage/inclusiveRange) + } + } + `) + + script2 := []byte(` + import "inclusive-range-creation" + + transaction { + prepare(signer: AuthAccount) { + let ir = signer.load>(from: /storage/inclusiveRange) + let containsFifteen = ir!.contains(15) + log(containsFifteen) + } + } + `) + + var loggedMessages []string + + ledger := newTestLedger(nil, nil) + + runtimeInterface := &testRuntimeInterface{ + getCode: func(location Location) (bytes []byte, err error) { + switch location { + case common.StringLocation("inclusive-range-creation"): + return inclusiveRangeCreation, nil + default: + return nil, fmt.Errorf("unknown import location: %s", location) + } + }, + storage: ledger, + getSigningAccounts: func() ([]Address, error) { + return []Address{{42}}, nil + }, + log: func(message string) { + loggedMessages = append(loggedMessages, message) + }, + } + + nextTransactionLocation := newTransactionLocationGenerator() + + err := runtime.ExecuteTransaction( + Script{ + Source: script1, + }, + Context{ + Interface: runtimeInterface, + Location: nextTransactionLocation(), + }, + ) + require.NoError(t, err) + + err = runtime.ExecuteTransaction( + Script{ + Source: script2, + }, + Context{ + Interface: runtimeInterface, + Location: nextTransactionLocation(), + }, + ) + require.NoError(t, err) + + assert.Contains(t, loggedMessages, "True") +} + func TestRuntimeResourceContractUseThroughReference(t *testing.T) { t.Parallel() From d80f4264eca0a0d816f736868611d5930d344f02 Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Thu, 5 Oct 2023 20:56:54 +0530 Subject: [PATCH 082/121] Revert the TestRuntimeStorageMultipleTransactionsInclusiveRangeFunction test --- runtime/runtime_test.go | 87 ----------------------------------------- 1 file changed, 87 deletions(-) diff --git a/runtime/runtime_test.go b/runtime/runtime_test.go index 061c3e10ed..8f1af02fb3 100644 --- a/runtime/runtime_test.go +++ b/runtime/runtime_test.go @@ -2365,93 +2365,6 @@ func TestRuntimeCompositeFunctionInvocationFromImportingProgram(t *testing.T) { require.NoError(t, err) } -// TestRuntimeStorageMultipleTransactionsInclusiveRangeFunction tests a function call -// of a stored inclusive range declared in an imported program -func TestRuntimeStorageMultipleTransactionsInclusiveRangeFunction(t *testing.T) { - - t.Parallel() - - runtime := newTestInterpreterRuntime() - - inclusiveRangeCreation := []byte(` - pub fun createInclusiveRange(): InclusiveRange { - return InclusiveRange(10, 20) - } - `) - - script1 := []byte(` - import "inclusive-range-creation" - - transaction { - - prepare(signer: AuthAccount) { - let ir = createInclusiveRange() - signer.save(ir, to: /storage/inclusiveRange) - } - } - `) - - script2 := []byte(` - import "inclusive-range-creation" - - transaction { - prepare(signer: AuthAccount) { - let ir = signer.load>(from: /storage/inclusiveRange) - let containsFifteen = ir!.contains(15) - log(containsFifteen) - } - } - `) - - var loggedMessages []string - - ledger := newTestLedger(nil, nil) - - runtimeInterface := &testRuntimeInterface{ - getCode: func(location Location) (bytes []byte, err error) { - switch location { - case common.StringLocation("inclusive-range-creation"): - return inclusiveRangeCreation, nil - default: - return nil, fmt.Errorf("unknown import location: %s", location) - } - }, - storage: ledger, - getSigningAccounts: func() ([]Address, error) { - return []Address{{42}}, nil - }, - log: func(message string) { - loggedMessages = append(loggedMessages, message) - }, - } - - nextTransactionLocation := newTransactionLocationGenerator() - - err := runtime.ExecuteTransaction( - Script{ - Source: script1, - }, - Context{ - Interface: runtimeInterface, - Location: nextTransactionLocation(), - }, - ) - require.NoError(t, err) - - err = runtime.ExecuteTransaction( - Script{ - Source: script2, - }, - Context{ - Interface: runtimeInterface, - Location: nextTransactionLocation(), - }, - ) - require.NoError(t, err) - - assert.Contains(t, loggedMessages, "True") -} - func TestRuntimeResourceContractUseThroughReference(t *testing.T) { t.Parallel() From 10f48af74ac2d6462e135b543f556ab50583dbc2 Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Thu, 5 Oct 2023 23:55:18 +0530 Subject: [PATCH 083/121] Add test case for comparing InclusiveRange --- runtime/sema/type.go | 4 ---- runtime/tests/checker/operations_test.go | 21 ++++++++++++++++++++- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/runtime/sema/type.go b/runtime/sema/type.go index e133aa6442..7d184cdfe9 100644 --- a/runtime/sema/type.go +++ b/runtime/sema/type.go @@ -5527,10 +5527,6 @@ func (t *InclusiveRangeType) initializeMemberResolvers() { }) } -func (t *InclusiveRangeType) ElementType(_ bool) Type { - return t.MemberType -} - func (*InclusiveRangeType) AllowsValueIndexingAssignment() bool { return false } diff --git a/runtime/tests/checker/operations_test.go b/runtime/tests/checker/operations_test.go index 8f9f9d3091..8f47388972 100644 --- a/runtime/tests/checker/operations_test.go +++ b/runtime/tests/checker/operations_test.go @@ -29,6 +29,7 @@ import ( "github.com/onflow/cadence/runtime/ast" "github.com/onflow/cadence/runtime/common" "github.com/onflow/cadence/runtime/sema" + "github.com/onflow/cadence/runtime/stdlib" ) func TestCheckInvalidUnaryBooleanNegationOfInteger(t *testing.T) { @@ -337,6 +338,9 @@ type operationWithTypeTests struct { func TestCheckNonIntegerComparisonOperations(t *testing.T) { t.Parallel() + baseValueActivation := sema.NewVariableActivation(sema.BaseValueActivation) + baseValueActivation.DeclareValue(stdlib.InclusiveRangeConstructorFunction) + allOperationTests := []operationWithTypeTests{ { operations: []ast.Operation{ @@ -363,6 +367,16 @@ func TestCheckNonIntegerComparisonOperations(t *testing.T) { {sema.BoolType, "1.2", "\"bcd\"", "Fix64", "String", []error{ &sema.InvalidBinaryOperandsError{}, }}, + { + sema.BoolType, + "InclusiveRange(1, 2)", + "InclusiveRange(3, 4)", + "InclusiveRange", + "InclusiveRange", + []error{ + &sema.InvalidBinaryOperandsError{}, + }, + }, }, }, } @@ -378,7 +392,7 @@ func TestCheckNonIntegerComparisonOperations(t *testing.T) { t.Run(testName, func(t *testing.T) { - _, err := ParseAndCheck(t, + _, err := ParseAndCheckWithOptions(t, fmt.Sprintf( `fun test(): %s { let a: %s = %s @@ -387,6 +401,11 @@ func TestCheckNonIntegerComparisonOperations(t *testing.T) { }`, test.ty, test.leftType, test.left, test.rightType, test.right, operation.Symbol(), ), + ParseAndCheckOptions{ + Config: &sema.Config{ + BaseValueActivation: baseValueActivation, + }, + }, ) errs := RequireCheckerErrors(t, err, len(test.expectedErrors)) From 64b1b0d42eb63af4ac9c045578b8adf0028d7b12 Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Thu, 5 Oct 2023 23:59:24 +0530 Subject: [PATCH 084/121] Add test case for inclusive range instantiation with 2 type args --- runtime/tests/checker/typeargument_test.go | 24 ++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/runtime/tests/checker/typeargument_test.go b/runtime/tests/checker/typeargument_test.go index 094128f07e..7167facadf 100644 --- a/runtime/tests/checker/typeargument_test.go +++ b/runtime/tests/checker/typeargument_test.go @@ -77,6 +77,30 @@ func TestCheckTypeArguments(t *testing.T) { assert.IsType(t, &sema.MissingTypeArgumentError{}, errs[0]) }) + t.Run("inclusive range, instantiation with more than arguments", func(t *testing.T) { + + t.Parallel() + + baseValueActivation := sema.NewVariableActivation(sema.BaseValueActivation) + baseValueActivation.DeclareValue(stdlib.InclusiveRangeConstructorFunction) + + _, err := ParseAndCheckWithOptions(t, + ` + let inclusiveRange: InclusiveRange = InclusiveRange(1, 10) + `, + ParseAndCheckOptions{ + Config: &sema.Config{ + BaseValueActivation: baseValueActivation, + }, + }, + ) + + errs := RequireCheckerErrors(t, err, 2) + + assert.IsType(t, &sema.InvalidTypeArgumentCountError{}, errs[0]) + assert.IsType(t, &sema.MissingTypeArgumentError{}, errs[1]) + }) + t.Run("capability, instantiation with no arguments", func(t *testing.T) { t.Parallel() From 4445c56a29e09b759ba9254aa94e51fe1a404310 Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Fri, 6 Oct 2023 22:19:37 +0530 Subject: [PATCH 085/121] Add test case to indicate unsupported storage of inclusive range --- runtime/runtime_test.go | 56 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/runtime/runtime_test.go b/runtime/runtime_test.go index 8f1af02fb3..5bd9789e03 100644 --- a/runtime/runtime_test.go +++ b/runtime/runtime_test.go @@ -2365,6 +2365,62 @@ func TestRuntimeCompositeFunctionInvocationFromImportingProgram(t *testing.T) { require.NoError(t, err) } +func TestRuntimeStorageMultipleTransactionsInclusiveRangeFunction(t *testing.T) { + + t.Parallel() + + runtime := newTestInterpreterRuntime() + + inclusiveRangeCreation := []byte(` + pub fun createInclusiveRange(): InclusiveRange { + return InclusiveRange(10, 20) + } + `) + + script1 := []byte(` + import "inclusive-range-creation" + transaction { + prepare(signer: AuthAccount) { + let ir = createInclusiveRange() + signer.save(ir, to: /storage/inclusiveRange) + } + } + `) + + ledger := newTestLedger(nil, nil) + + runtimeInterface := &testRuntimeInterface{ + getCode: func(location Location) (bytes []byte, err error) { + switch location { + case common.StringLocation("inclusive-range-creation"): + return inclusiveRangeCreation, nil + default: + return nil, fmt.Errorf("unknown import location: %s", location) + } + }, + storage: ledger, + getSigningAccounts: func() ([]Address, error) { + return []Address{{42}}, nil + }, + } + + nextTransactionLocation := newTransactionLocationGenerator() + + err := runtime.ExecuteTransaction( + Script{ + Source: script1, + }, + Context{ + Interface: runtimeInterface, + Location: nextTransactionLocation(), + }, + ) + RequireError(t, err) + + var subErr *atree.ExternalError + require.ErrorAs(t, err, &subErr) +} + func TestRuntimeResourceContractUseThroughReference(t *testing.T) { t.Parallel() From db22c2cc60c1e7b5a09710d3e9076558164a12c1 Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Sat, 7 Oct 2023 22:01:38 +0530 Subject: [PATCH 086/121] Make InclusiveRange type non-storable --- runtime/runtime_test.go | 11 +++++++++-- runtime/sema/type.go | 2 +- runtime/tests/checker/storable_test.go | 1 + 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/runtime/runtime_test.go b/runtime/runtime_test.go index 5bd9789e03..478876289b 100644 --- a/runtime/runtime_test.go +++ b/runtime/runtime_test.go @@ -2417,8 +2417,15 @@ func TestRuntimeStorageMultipleTransactionsInclusiveRangeFunction(t *testing.T) ) RequireError(t, err) - var subErr *atree.ExternalError - require.ErrorAs(t, err, &subErr) + var checkerErr *sema.CheckerError + require.ErrorAs(t, err, &checkerErr) + + errs := checker.RequireCheckerErrors(t, checkerErr, 1) + + assert.IsType(t, &sema.TypeMismatchError{}, errs[0]) + + typeMismatchError := errs[0].(*sema.TypeMismatchError) + assert.Contains(t, typeMismatchError.SecondaryError(), "expected `Storable`, got `InclusiveRange`") } func TestRuntimeResourceContractUseThroughReference(t *testing.T) { diff --git a/runtime/sema/type.go b/runtime/sema/type.go index 7d184cdfe9..4fe8cb6400 100644 --- a/runtime/sema/type.go +++ b/runtime/sema/type.go @@ -5352,7 +5352,7 @@ func (t *InclusiveRangeType) IsInvalidType() bool { } func (t *InclusiveRangeType) IsStorable(results map[*Member]bool) bool { - return t.MemberType.IsStorable(results) + return false } func (t *InclusiveRangeType) IsExportable(results map[*Member]bool) bool { diff --git a/runtime/tests/checker/storable_test.go b/runtime/tests/checker/storable_test.go index 3f90ba4ecf..f256154bfa 100644 --- a/runtime/tests/checker/storable_test.go +++ b/runtime/tests/checker/storable_test.go @@ -133,6 +133,7 @@ func TestCheckStorable(t *testing.T) { sema.VoidType, sema.AuthAccountType, sema.PublicAccountType, + &sema.InclusiveRangeType{MemberType: sema.IntType}, } // Capabilities of non-storable types are storable From 1ca3adcffab241c9c2e28c295628b8a3149de89e Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Mon, 9 Oct 2023 21:47:36 +0530 Subject: [PATCH 087/121] Remove auth account storage test for InclusiveRange --- runtime/tests/interpreter/account_test.go | 50 ----------------------- 1 file changed, 50 deletions(-) diff --git a/runtime/tests/interpreter/account_test.go b/runtime/tests/interpreter/account_test.go index c5c2ba29df..caa6bf5087 100644 --- a/runtime/tests/interpreter/account_test.go +++ b/runtime/tests/interpreter/account_test.go @@ -503,56 +503,6 @@ func TestInterpretAuthAccount_load(t *testing.T) { require.Len(t, getAccountValues(), 1) }) }) - - t.Run("inclusiverange with contains", func(t *testing.T) { - - t.Parallel() - - address := interpreter.NewUnmeteredAddressValueFromBytes([]byte{42}) - - inter, getAccountValues := testAccount( - t, - address, - true, - ` - fun save() { - let ir = InclusiveRange(10, 20) - account.save(ir, to: /storage/ir) - } - - fun loadIRAndCallContains(): InclusiveRange? { - let ir = account.load>(from: /storage/ir) - let containsFifteen = ir!.contains(15) - return ir - } - `, - sema.Config{}, - ) - - t.Run("save and load inclusiverange. call contains", func(t *testing.T) { - - // save - - _, err := inter.Invoke("save") - require.NoError(t, err) - - require.Len(t, getAccountValues(), 1) - - // first load - - value, err := inter.Invoke("loadIRAndCallContains") - require.NoError(t, err) - - require.IsType(t, &interpreter.SomeValue{}, value) - - innerValue := value.(*interpreter.SomeValue).InnerValue(inter, interpreter.EmptyLocationRange) - - assert.IsType(t, &interpreter.CompositeValue{}, innerValue) - - // NOTE: check loaded value was removed from storage - require.Len(t, getAccountValues(), 0) - }) - }) } func TestInterpretAuthAccount_copy(t *testing.T) { From 19eb821338813e45b82204895b491bf82ca7073b Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Sun, 22 Oct 2023 23:47:21 +0530 Subject: [PATCH 088/121] Allow InclusiveRange in for-in loop --- runtime/interpreter/value.go | 63 +++++++++++++++ runtime/interpreter/value_range.go | 8 +- runtime/sema/check_for.go | 2 + runtime/tests/checker/for_test.go | 33 ++++++++ runtime/tests/interpreter/for_test.go | 109 ++++++++++++++++++++++++++ 5 files changed, 211 insertions(+), 4 deletions(-) diff --git a/runtime/interpreter/value.go b/runtime/interpreter/value.go index 1fd6503f34..92cd36d5c4 100644 --- a/runtime/interpreter/value.go +++ b/runtime/interpreter/value.go @@ -16267,6 +16267,7 @@ type CompositeField struct { const attachmentNamePrefix = "$" var _ TypeIndexableValue = &CompositeValue{} +var _ IterableValue = &CompositeValue{} func NewCompositeField(memoryGauge common.MemoryGauge, name string, value Value) CompositeField { common.UseMemory(memoryGauge, common.CompositeFieldMemoryUsage) @@ -17782,6 +17783,68 @@ func (v *CompositeValue) RemoveTypeKey( return v.RemoveMember(interpreter, locationRange, attachmentMemberName(attachmentType)) } +func (v *CompositeValue) Iterator(interpreter *Interpreter) ValueIterator { + staticType := v.StaticType(interpreter) + + switch staticType.(type) { + case InclusiveRangeStaticType: + startValue := v.GetField(interpreter, EmptyLocationRange, sema.InclusiveRangeTypeStartFieldName).(IntegerValue) + return &InclusiveRangeIterator{ + rangeValue: v, + next: startValue, + } + + default: + // Must be caught in the checker. + panic(errors.NewUnreachableError()) + } +} + +type InclusiveRangeIterator struct { + rangeValue *CompositeValue + next IntegerValue +} + +var _ ValueIterator = &InclusiveRangeIterator{} + +func (i *InclusiveRangeIterator) Next(interpreter *Interpreter) Value { + valueToReturn := i.next + + end := GetFieldAsIntegerValue(i.rangeValue, interpreter, EmptyLocationRange, sema.InclusiveRangeTypeEndFieldName) + step := GetFieldAsIntegerValue(i.rangeValue, interpreter, EmptyLocationRange, sema.InclusiveRangeTypeStepFieldName) + + staticType := i.rangeValue.StaticType(interpreter) + var elementType StaticType + switch typ := staticType.(type) { + case InclusiveRangeStaticType: + elementType = typ.ElementType + default: + panic(errors.NewUnreachableError()) + } + + zeroValue := GetSmallIntegerValue(0, elementType) + + // Ensure that valueToReturn is within the bounds. + if step.Less(interpreter, zeroValue, EmptyLocationRange) { + if valueToReturn.Less(interpreter, end, EmptyLocationRange) { + return nil + } + } else { + if valueToReturn.Greater(interpreter, end, EmptyLocationRange) { + return nil + } + } + + // Update the next value. + nextValueToReturn, ok := valueToReturn.Plus(interpreter, step, EmptyLocationRange).(IntegerValue) + if !ok { + panic(errors.NewUnreachableError()) + } + + i.next = nextValueToReturn + return valueToReturn +} + // DictionaryValue type DictionaryValue struct { diff --git a/runtime/interpreter/value_range.go b/runtime/interpreter/value_range.go index 835caac068..5840bbd589 100644 --- a/runtime/interpreter/value_range.go +++ b/runtime/interpreter/value_range.go @@ -189,9 +189,9 @@ func rangeContains( locationRange LocationRange, needleValue IntegerValue, ) BoolValue { - start := getFieldAsIntegerValue(rangeValue, interpreter, locationRange, sema.InclusiveRangeTypeStartFieldName) - end := getFieldAsIntegerValue(rangeValue, interpreter, locationRange, sema.InclusiveRangeTypeEndFieldName) - step := getFieldAsIntegerValue(rangeValue, interpreter, locationRange, sema.InclusiveRangeTypeStepFieldName) + start := GetFieldAsIntegerValue(rangeValue, interpreter, locationRange, sema.InclusiveRangeTypeStartFieldName) + end := GetFieldAsIntegerValue(rangeValue, interpreter, locationRange, sema.InclusiveRangeTypeEndFieldName) + step := GetFieldAsIntegerValue(rangeValue, interpreter, locationRange, sema.InclusiveRangeTypeStepFieldName) result := start.Equal(interpreter, locationRange, needleValue) || end.Equal(interpreter, locationRange, needleValue) @@ -219,7 +219,7 @@ func rangeContains( return AsBoolValue(result) } -func getFieldAsIntegerValue( +func GetFieldAsIntegerValue( rangeValue *CompositeValue, interpreter *Interpreter, locationRange LocationRange, diff --git a/runtime/sema/check_for.go b/runtime/sema/check_for.go index 1d8a4aae4e..a92af28665 100644 --- a/runtime/sema/check_for.go +++ b/runtime/sema/check_for.go @@ -62,6 +62,8 @@ func (checker *Checker) VisitForStatement(statement *ast.ForStatement) (_ struct elementType = arrayType.ElementType(false) } else if valueType == StringType { elementType = CharacterType + } else if inclusiveRangeType, ok := valueType.(*InclusiveRangeType); ok { + elementType = inclusiveRangeType.MemberType } else { checker.report( &TypeMismatchWithDescriptionError{ diff --git a/runtime/tests/checker/for_test.go b/runtime/tests/checker/for_test.go index 9ed443c605..56f75147f3 100644 --- a/runtime/tests/checker/for_test.go +++ b/runtime/tests/checker/for_test.go @@ -19,11 +19,13 @@ package checker import ( + "fmt" "testing" "github.com/stretchr/testify/assert" "github.com/onflow/cadence/runtime/sema" + "github.com/onflow/cadence/runtime/stdlib" ) func TestCheckForVariableSized(t *testing.T) { @@ -76,6 +78,37 @@ func TestCheckForString(t *testing.T) { assert.NoError(t, err) } +func TestCheckForInclusiveRange(t *testing.T) { + + t.Parallel() + + baseValueActivation := sema.NewVariableActivation(sema.BaseValueActivation) + baseValueActivation.DeclareValue(stdlib.InclusiveRangeConstructorFunction) + + for _, typ := range sema.AllIntegerTypes { + code := fmt.Sprintf(` + fun test() { + let s : %[1]s = 1 + let e : %[1]s = 2 + let step : %[1]s = 1 + let r: InclusiveRange<%[1]s> = InclusiveRange(s, e, step: step) + + for c in r {} + } + `, typ.String()) + + _, err := ParseAndCheckWithOptions(t, code, + ParseAndCheckOptions{ + Config: &sema.Config{ + BaseValueActivation: baseValueActivation, + }, + }, + ) + + assert.NoError(t, err) + } +} + func TestCheckForEmpty(t *testing.T) { t.Parallel() diff --git a/runtime/tests/interpreter/for_test.go b/runtime/tests/interpreter/for_test.go index c3c2b22e98..3d43cff77d 100644 --- a/runtime/tests/interpreter/for_test.go +++ b/runtime/tests/interpreter/for_test.go @@ -19,11 +19,15 @@ package interpreter_test import ( + "fmt" "testing" "github.com/stretchr/testify/require" + "github.com/onflow/cadence/runtime/activations" "github.com/onflow/cadence/runtime/common" + "github.com/onflow/cadence/runtime/sema" + "github.com/onflow/cadence/runtime/stdlib" . "github.com/onflow/cadence/runtime/tests/utils" "github.com/onflow/cadence/runtime/interpreter" @@ -255,3 +259,108 @@ func TestInterpretForString(t *testing.T) { value, ) } + +type inclusiveRangeForInLoopTest struct { + s, e, step int8 + expectedLoopCount int + onlySignedTest bool +} + +func TestInclusiveRangeForInLoop(t *testing.T) { + t.Parallel() + + baseValueActivation := sema.NewVariableActivation(sema.BaseValueActivation) + baseValueActivation.DeclareValue(stdlib.InclusiveRangeConstructorFunction) + + baseActivation := activations.NewActivation(nil, interpreter.BaseActivation) + interpreter.Declare(baseActivation, stdlib.InclusiveRangeConstructorFunction) + + testCases := []inclusiveRangeForInLoopTest{ + { + s: 0, + e: 10, + step: 1, + expectedLoopCount: 11, + }, + { + s: 0, + e: 10, + step: 2, + expectedLoopCount: 6, + }, + { + s: 10, + e: -10, + step: -2, + expectedLoopCount: 11, + onlySignedTest: true, + }, + } + + runTestCase := func(t *testing.T, typ sema.Type, testCase inclusiveRangeForInLoopTest) { + t.Run(typ.String(), func(t *testing.T) { + t.Parallel() + + code := fmt.Sprintf( + ` + fun test(): Int { + let s : %[1]s = %[2]d + let e : %[1]s = %[3]d + let step : %[1]s = %[4]d + let r: InclusiveRange<%[1]s> = InclusiveRange(s, e, step: step) + + var count = 0 + for c in r { + count = count + 1 + } + return count + } + `, + typ.String(), + testCase.s, + testCase.e, + testCase.step, + ) + + inter, err := parseCheckAndInterpretWithOptions(t, code, + ParseCheckAndInterpretOptions{ + CheckerConfig: &sema.Config{ + BaseValueActivation: baseValueActivation, + }, + Config: &interpreter.Config{ + BaseActivation: baseActivation, + }, + }, + ) + + require.NoError(t, err) + countValue, err := inter.Invoke("test") + require.NoError(t, err) + + AssertValuesEqual( + t, + inter, + interpreter.NewIntValueFromInt64(nil, int64(testCase.expectedLoopCount)), + countValue, + ) + }) + } + + for _, typ := range sema.AllIntegerTypes { + for _, testCase := range testCases { + if testCase.onlySignedTest { + continue + } + runTestCase(t, typ, testCase) + } + } + + for _, typ := range sema.AllSignedIntegerTypes { + for _, testCase := range testCases { + if !testCase.onlySignedTest { + continue + } + runTestCase(t, typ, testCase) + } + } +} From ea8ecd4be0da6cd013657dcf0b810773bd4dad51 Mon Sep 17 00:00:00 2001 From: darkdrag00nv2 <122124396+darkdrag00nv2@users.noreply.github.com> Date: Thu, 2 Nov 2023 20:46:28 +0530 Subject: [PATCH 089/121] use meaningful names Co-authored-by: Supun Setunga --- runtime/tests/checker/for_test.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/runtime/tests/checker/for_test.go b/runtime/tests/checker/for_test.go index 56f75147f3..5746d2d586 100644 --- a/runtime/tests/checker/for_test.go +++ b/runtime/tests/checker/for_test.go @@ -88,12 +88,14 @@ func TestCheckForInclusiveRange(t *testing.T) { for _, typ := range sema.AllIntegerTypes { code := fmt.Sprintf(` fun test() { - let s : %[1]s = 1 - let e : %[1]s = 2 + let start : %[1]s = 1 + let end : %[1]s = 2 let step : %[1]s = 1 - let r: InclusiveRange<%[1]s> = InclusiveRange(s, e, step: step) + let range: InclusiveRange<%[1]s> = InclusiveRange(start, end, step: step) - for c in r {} + for value in range { + var typedValue: %[1]s = value + } } `, typ.String()) From cfefca339d816953a75b0401a440ee23cd3942f4 Mon Sep 17 00:00:00 2001 From: darkdrag00nv2 <122124396+darkdrag00nv2@users.noreply.github.com> Date: Thu, 2 Nov 2023 20:46:53 +0530 Subject: [PATCH 090/121] Use meaningful names in inclusiveRangeForInLoopTest struct Co-authored-by: Supun Setunga --- runtime/tests/interpreter/for_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/tests/interpreter/for_test.go b/runtime/tests/interpreter/for_test.go index 3d43cff77d..338767f132 100644 --- a/runtime/tests/interpreter/for_test.go +++ b/runtime/tests/interpreter/for_test.go @@ -261,7 +261,7 @@ func TestInterpretForString(t *testing.T) { } type inclusiveRangeForInLoopTest struct { - s, e, step int8 + start, end, step int8 expectedLoopCount int onlySignedTest bool } From 2823012f2d2f899457fb7fc8ba36e1229c67766d Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Thu, 2 Nov 2023 20:41:31 +0530 Subject: [PATCH 091/121] Remove export of getFieldAsIntegerValue --- runtime/interpreter/value.go | 4 ++-- runtime/interpreter/value_range.go | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/runtime/interpreter/value.go b/runtime/interpreter/value.go index 92cd36d5c4..fb394a3b3c 100644 --- a/runtime/interpreter/value.go +++ b/runtime/interpreter/value.go @@ -17810,8 +17810,8 @@ var _ ValueIterator = &InclusiveRangeIterator{} func (i *InclusiveRangeIterator) Next(interpreter *Interpreter) Value { valueToReturn := i.next - end := GetFieldAsIntegerValue(i.rangeValue, interpreter, EmptyLocationRange, sema.InclusiveRangeTypeEndFieldName) - step := GetFieldAsIntegerValue(i.rangeValue, interpreter, EmptyLocationRange, sema.InclusiveRangeTypeStepFieldName) + end := getFieldAsIntegerValue(i.rangeValue, interpreter, EmptyLocationRange, sema.InclusiveRangeTypeEndFieldName) + step := getFieldAsIntegerValue(i.rangeValue, interpreter, EmptyLocationRange, sema.InclusiveRangeTypeStepFieldName) staticType := i.rangeValue.StaticType(interpreter) var elementType StaticType diff --git a/runtime/interpreter/value_range.go b/runtime/interpreter/value_range.go index 5840bbd589..835caac068 100644 --- a/runtime/interpreter/value_range.go +++ b/runtime/interpreter/value_range.go @@ -189,9 +189,9 @@ func rangeContains( locationRange LocationRange, needleValue IntegerValue, ) BoolValue { - start := GetFieldAsIntegerValue(rangeValue, interpreter, locationRange, sema.InclusiveRangeTypeStartFieldName) - end := GetFieldAsIntegerValue(rangeValue, interpreter, locationRange, sema.InclusiveRangeTypeEndFieldName) - step := GetFieldAsIntegerValue(rangeValue, interpreter, locationRange, sema.InclusiveRangeTypeStepFieldName) + start := getFieldAsIntegerValue(rangeValue, interpreter, locationRange, sema.InclusiveRangeTypeStartFieldName) + end := getFieldAsIntegerValue(rangeValue, interpreter, locationRange, sema.InclusiveRangeTypeEndFieldName) + step := getFieldAsIntegerValue(rangeValue, interpreter, locationRange, sema.InclusiveRangeTypeStepFieldName) result := start.Equal(interpreter, locationRange, needleValue) || end.Equal(interpreter, locationRange, needleValue) @@ -219,7 +219,7 @@ func rangeContains( return AsBoolValue(result) } -func GetFieldAsIntegerValue( +func getFieldAsIntegerValue( rangeValue *CompositeValue, interpreter *Interpreter, locationRange LocationRange, From 86b36e8e4fed390deee2a609eb21f3a9b2b75cea Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Thu, 2 Nov 2023 21:39:15 +0530 Subject: [PATCH 092/121] Asset loop elements instead of just the count --- runtime/tests/interpreter/for_test.go | 111 ++++++++++++++++---------- 1 file changed, 70 insertions(+), 41 deletions(-) diff --git a/runtime/tests/interpreter/for_test.go b/runtime/tests/interpreter/for_test.go index 338767f132..30e751dc9a 100644 --- a/runtime/tests/interpreter/for_test.go +++ b/runtime/tests/interpreter/for_test.go @@ -22,6 +22,7 @@ import ( "fmt" "testing" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/onflow/cadence/runtime/activations" @@ -261,9 +262,8 @@ func TestInterpretForString(t *testing.T) { } type inclusiveRangeForInLoopTest struct { - start, end, step int8 - expectedLoopCount int - onlySignedTest bool + start, end, step int8 + loopElements []int } func TestInclusiveRangeForInLoop(t *testing.T) { @@ -275,25 +275,27 @@ func TestInclusiveRangeForInLoop(t *testing.T) { baseActivation := activations.NewActivation(nil, interpreter.BaseActivation) interpreter.Declare(baseActivation, stdlib.InclusiveRangeConstructorFunction) - testCases := []inclusiveRangeForInLoopTest{ + unsignedTestCases := []inclusiveRangeForInLoopTest{ { - s: 0, - e: 10, - step: 1, - expectedLoopCount: 11, + start: 0, + end: 10, + step: 1, + loopElements: []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, }, { - s: 0, - e: 10, - step: 2, - expectedLoopCount: 6, + start: 0, + end: 10, + step: 2, + loopElements: []int{0, 2, 4, 6, 8, 10}, }, + } + + signedTestCases := []inclusiveRangeForInLoopTest{ { - s: 10, - e: -10, - step: -2, - expectedLoopCount: 11, - onlySignedTest: true, + start: 10, + end: -10, + step: -2, + loopElements: []int{10, 8, 6, 4, 2, 0, -2, -4, -6, -8, -10}, }, } @@ -303,22 +305,22 @@ func TestInclusiveRangeForInLoop(t *testing.T) { code := fmt.Sprintf( ` - fun test(): Int { - let s : %[1]s = %[2]d - let e : %[1]s = %[3]d + fun test(): [%[1]s] { + let start : %[1]s = %[2]d + let end : %[1]s = %[3]d let step : %[1]s = %[4]d - let r: InclusiveRange<%[1]s> = InclusiveRange(s, e, step: step) + let range: InclusiveRange<%[1]s> = InclusiveRange(start, end, step: step) - var count = 0 - for c in r { - count = count + 1 + var elements : [%[1]s] = [] + for element in range { + elements.append(element) } - return count + return elements } `, typ.String(), - testCase.s, - testCase.e, + testCase.start, + testCase.end, testCase.step, ) @@ -334,32 +336,59 @@ func TestInclusiveRangeForInLoop(t *testing.T) { ) require.NoError(t, err) - countValue, err := inter.Invoke("test") + loopElements, err := inter.Invoke("test") require.NoError(t, err) - AssertValuesEqual( - t, - inter, - interpreter.NewIntValueFromInt64(nil, int64(testCase.expectedLoopCount)), - countValue, + integerStaticType := interpreter.ConvertSemaToStaticType( + nil, + typ, ) + + count := 0 + iterator := (loopElements).(*interpreter.ArrayValue).Iterator(inter) + for { + elem := iterator.Next(inter) + if elem == nil { + break + } + + AssertValuesEqual( + t, + inter, + interpreter.GetSmallIntegerValue( + int8(testCase.loopElements[count]), + integerStaticType, + ), + elem, + ) + + count += 1 + } + + assert.Equal(t, len(testCase.loopElements), count) }) } for _, typ := range sema.AllIntegerTypes { - for _, testCase := range testCases { - if testCase.onlySignedTest { - continue - } + // Only test leaf types + switch typ { + case sema.IntegerType, sema.SignedIntegerType: + continue + } + + for _, testCase := range unsignedTestCases { runTestCase(t, typ, testCase) } } for _, typ := range sema.AllSignedIntegerTypes { - for _, testCase := range testCases { - if !testCase.onlySignedTest { - continue - } + // Only test leaf types + switch typ { + case sema.SignedIntegerType: + continue + } + + for _, testCase := range signedTestCases { runTestCase(t, typ, testCase) } } From d21296ea93f2994d090d08dd8f57afb30e676d42 Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Thu, 2 Nov 2023 21:52:37 +0530 Subject: [PATCH 093/121] Introduce constructor for InclusiveRangeIterator --- runtime/interpreter/value.go | 54 ++++++++++++++++++------------------ 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/runtime/interpreter/value.go b/runtime/interpreter/value.go index fb394a3b3c..75c99eca0b 100644 --- a/runtime/interpreter/value.go +++ b/runtime/interpreter/value.go @@ -17786,13 +17786,9 @@ func (v *CompositeValue) RemoveTypeKey( func (v *CompositeValue) Iterator(interpreter *Interpreter) ValueIterator { staticType := v.StaticType(interpreter) - switch staticType.(type) { + switch typ := staticType.(type) { case InclusiveRangeStaticType: - startValue := v.GetField(interpreter, EmptyLocationRange, sema.InclusiveRangeTypeStartFieldName).(IntegerValue) - return &InclusiveRangeIterator{ - rangeValue: v, - next: startValue, - } + return NewInclusiveRangeIterator(v, interpreter, typ) default: // Must be caught in the checker. @@ -17801,38 +17797,42 @@ func (v *CompositeValue) Iterator(interpreter *Interpreter) ValueIterator { } type InclusiveRangeIterator struct { - rangeValue *CompositeValue - next IntegerValue + rangeValue *CompositeValue + next IntegerValue + stepNegative bool } var _ ValueIterator = &InclusiveRangeIterator{} +func NewInclusiveRangeIterator( + v *CompositeValue, + interpreter *Interpreter, + typ InclusiveRangeStaticType, +) *InclusiveRangeIterator { + startValue := v.GetField(interpreter, EmptyLocationRange, sema.InclusiveRangeTypeStartFieldName).(IntegerValue) + + zeroValue := GetSmallIntegerValue(0, typ.ElementType) + stepValue := v.GetField(interpreter, EmptyLocationRange, sema.InclusiveRangeTypeStepFieldName).(IntegerValue) + stepNegative := stepValue.Less(interpreter, zeroValue, EmptyLocationRange) + + return &InclusiveRangeIterator{ + rangeValue: v, + next: startValue, + stepNegative: bool(stepNegative), + } +} + func (i *InclusiveRangeIterator) Next(interpreter *Interpreter) Value { valueToReturn := i.next end := getFieldAsIntegerValue(i.rangeValue, interpreter, EmptyLocationRange, sema.InclusiveRangeTypeEndFieldName) step := getFieldAsIntegerValue(i.rangeValue, interpreter, EmptyLocationRange, sema.InclusiveRangeTypeStepFieldName) - staticType := i.rangeValue.StaticType(interpreter) - var elementType StaticType - switch typ := staticType.(type) { - case InclusiveRangeStaticType: - elementType = typ.ElementType - default: - panic(errors.NewUnreachableError()) - } - - zeroValue := GetSmallIntegerValue(0, elementType) - // Ensure that valueToReturn is within the bounds. - if step.Less(interpreter, zeroValue, EmptyLocationRange) { - if valueToReturn.Less(interpreter, end, EmptyLocationRange) { - return nil - } - } else { - if valueToReturn.Greater(interpreter, end, EmptyLocationRange) { - return nil - } + if i.stepNegative && bool(valueToReturn.Less(interpreter, end, EmptyLocationRange)) { + return nil + } else if !i.stepNegative && bool(valueToReturn.Greater(interpreter, end, EmptyLocationRange)) { + return nil } // Update the next value. From 59b1c580609d867f88b09846c5ca6857aac8f662 Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Fri, 3 Nov 2023 21:11:29 +0530 Subject: [PATCH 094/121] Review suggestions --- runtime/interpreter/value.go | 27 ++++++++++++++++----------- runtime/interpreter/value_range.go | 8 ++++---- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/runtime/interpreter/value.go b/runtime/interpreter/value.go index 75c99eca0b..16579a2132 100644 --- a/runtime/interpreter/value.go +++ b/runtime/interpreter/value.go @@ -17788,7 +17788,7 @@ func (v *CompositeValue) Iterator(interpreter *Interpreter) ValueIterator { switch typ := staticType.(type) { case InclusiveRangeStaticType: - return NewInclusiveRangeIterator(v, interpreter, typ) + return NewInclusiveRangeIterator(interpreter, v, typ) default: // Must be caught in the checker. @@ -17797,46 +17797,51 @@ func (v *CompositeValue) Iterator(interpreter *Interpreter) ValueIterator { } type InclusiveRangeIterator struct { - rangeValue *CompositeValue - next IntegerValue + rangeValue *CompositeValue + next IntegerValue + + // Cached values stepNegative bool + step IntegerValue + end IntegerValue } var _ ValueIterator = &InclusiveRangeIterator{} func NewInclusiveRangeIterator( - v *CompositeValue, interpreter *Interpreter, + v *CompositeValue, typ InclusiveRangeStaticType, ) *InclusiveRangeIterator { startValue := v.GetField(interpreter, EmptyLocationRange, sema.InclusiveRangeTypeStartFieldName).(IntegerValue) zeroValue := GetSmallIntegerValue(0, typ.ElementType) - stepValue := v.GetField(interpreter, EmptyLocationRange, sema.InclusiveRangeTypeStepFieldName).(IntegerValue) + endValue := getFieldAsIntegerValue(interpreter, v, EmptyLocationRange, sema.InclusiveRangeTypeEndFieldName) + + stepValue := getFieldAsIntegerValue(interpreter, v, EmptyLocationRange, sema.InclusiveRangeTypeStepFieldName) stepNegative := stepValue.Less(interpreter, zeroValue, EmptyLocationRange) return &InclusiveRangeIterator{ rangeValue: v, next: startValue, stepNegative: bool(stepNegative), + step: stepValue, + end: endValue, } } func (i *InclusiveRangeIterator) Next(interpreter *Interpreter) Value { valueToReturn := i.next - end := getFieldAsIntegerValue(i.rangeValue, interpreter, EmptyLocationRange, sema.InclusiveRangeTypeEndFieldName) - step := getFieldAsIntegerValue(i.rangeValue, interpreter, EmptyLocationRange, sema.InclusiveRangeTypeStepFieldName) - // Ensure that valueToReturn is within the bounds. - if i.stepNegative && bool(valueToReturn.Less(interpreter, end, EmptyLocationRange)) { + if i.stepNegative && bool(valueToReturn.Less(interpreter, i.end, EmptyLocationRange)) { return nil - } else if !i.stepNegative && bool(valueToReturn.Greater(interpreter, end, EmptyLocationRange)) { + } else if !i.stepNegative && bool(valueToReturn.Greater(interpreter, i.end, EmptyLocationRange)) { return nil } // Update the next value. - nextValueToReturn, ok := valueToReturn.Plus(interpreter, step, EmptyLocationRange).(IntegerValue) + nextValueToReturn, ok := valueToReturn.Plus(interpreter, i.step, EmptyLocationRange).(IntegerValue) if !ok { panic(errors.NewUnreachableError()) } diff --git a/runtime/interpreter/value_range.go b/runtime/interpreter/value_range.go index 835caac068..566cef02d2 100644 --- a/runtime/interpreter/value_range.go +++ b/runtime/interpreter/value_range.go @@ -189,9 +189,9 @@ func rangeContains( locationRange LocationRange, needleValue IntegerValue, ) BoolValue { - start := getFieldAsIntegerValue(rangeValue, interpreter, locationRange, sema.InclusiveRangeTypeStartFieldName) - end := getFieldAsIntegerValue(rangeValue, interpreter, locationRange, sema.InclusiveRangeTypeEndFieldName) - step := getFieldAsIntegerValue(rangeValue, interpreter, locationRange, sema.InclusiveRangeTypeStepFieldName) + start := getFieldAsIntegerValue(interpreter, rangeValue, locationRange, sema.InclusiveRangeTypeStartFieldName) + end := getFieldAsIntegerValue(interpreter, rangeValue, locationRange, sema.InclusiveRangeTypeEndFieldName) + step := getFieldAsIntegerValue(interpreter, rangeValue, locationRange, sema.InclusiveRangeTypeStepFieldName) result := start.Equal(interpreter, locationRange, needleValue) || end.Equal(interpreter, locationRange, needleValue) @@ -220,8 +220,8 @@ func rangeContains( } func getFieldAsIntegerValue( - rangeValue *CompositeValue, interpreter *Interpreter, + rangeValue *CompositeValue, locationRange LocationRange, name string, ) IntegerValue { From 52ced9be10ad6759a69f02de8c3eb242f49493db Mon Sep 17 00:00:00 2001 From: darkdrag00nv2 <122124396+darkdrag00nv2@users.noreply.github.com> Date: Tue, 7 Nov 2023 18:00:58 +0530 Subject: [PATCH 095/121] Use getFieldAsIntegerValue Co-authored-by: Supun Setunga --- runtime/interpreter/value.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/interpreter/value.go b/runtime/interpreter/value.go index 16579a2132..6b6f2edd7b 100644 --- a/runtime/interpreter/value.go +++ b/runtime/interpreter/value.go @@ -17813,7 +17813,7 @@ func NewInclusiveRangeIterator( v *CompositeValue, typ InclusiveRangeStaticType, ) *InclusiveRangeIterator { - startValue := v.GetField(interpreter, EmptyLocationRange, sema.InclusiveRangeTypeStartFieldName).(IntegerValue) + startValue := getFieldAsIntegerValue(interpreter, EmptyLocationRange, sema.InclusiveRangeTypeStartFieldName) zeroValue := GetSmallIntegerValue(0, typ.ElementType) endValue := getFieldAsIntegerValue(interpreter, v, EmptyLocationRange, sema.InclusiveRangeTypeEndFieldName) From 63a2ee477cc72abddd62e90f13287eba7e464aa2 Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Tue, 7 Nov 2023 18:11:00 +0530 Subject: [PATCH 096/121] Pass correct location range from call-site --- runtime/interpreter/interpreter_statement.go | 2 +- runtime/interpreter/value.go | 27 +++++++++++--------- runtime/tests/interpreter/for_test.go | 2 +- 3 files changed, 17 insertions(+), 14 deletions(-) diff --git a/runtime/interpreter/interpreter_statement.go b/runtime/interpreter/interpreter_statement.go index 171748e389..60260a4b3e 100644 --- a/runtime/interpreter/interpreter_statement.go +++ b/runtime/interpreter/interpreter_statement.go @@ -331,7 +331,7 @@ func (interpreter *Interpreter) VisitForStatement(statement *ast.ForStatement) S panic(errors.NewUnreachableError()) } - iterator := iterable.Iterator(interpreter) + iterator := iterable.Iterator(interpreter, locationRange) var indexVariable *Variable if statement.Index != nil { diff --git a/runtime/interpreter/value.go b/runtime/interpreter/value.go index 6b6f2edd7b..c95269c988 100644 --- a/runtime/interpreter/value.go +++ b/runtime/interpreter/value.go @@ -246,7 +246,7 @@ type LinkValue interface { // IterableValue is a value which can be iterated over, e.g. with a for-loop type IterableValue interface { Value - Iterator(interpreter *Interpreter) ValueIterator + Iterator(interpreter *Interpreter, locationRange LocationRange) ValueIterator } // ValueIterator is an iterator which returns values. @@ -1516,7 +1516,7 @@ func (v *StringValue) ConformsToStaticType( return true } -func (v *StringValue) Iterator(_ *Interpreter) ValueIterator { +func (v *StringValue) Iterator(_ *Interpreter, _ LocationRange) ValueIterator { return StringValueIterator{ graphemes: uniseg.NewGraphemes(v.Str), } @@ -1550,7 +1550,7 @@ type ArrayValueIterator struct { atreeIterator *atree.ArrayIterator } -func (v *ArrayValue) Iterator(_ *Interpreter) ValueIterator { +func (v *ArrayValue) Iterator(_ *Interpreter, _ LocationRange) ValueIterator { arrayIterator, err := v.array.Iterator() if err != nil { panic(errors.NewExternalError(err)) @@ -17783,12 +17783,12 @@ func (v *CompositeValue) RemoveTypeKey( return v.RemoveMember(interpreter, locationRange, attachmentMemberName(attachmentType)) } -func (v *CompositeValue) Iterator(interpreter *Interpreter) ValueIterator { +func (v *CompositeValue) Iterator(interpreter *Interpreter, locationRange LocationRange) ValueIterator { staticType := v.StaticType(interpreter) switch typ := staticType.(type) { case InclusiveRangeStaticType: - return NewInclusiveRangeIterator(interpreter, v, typ) + return NewInclusiveRangeIterator(interpreter, locationRange, v, typ) default: // Must be caught in the checker. @@ -17804,22 +17804,25 @@ type InclusiveRangeIterator struct { stepNegative bool step IntegerValue end IntegerValue + + locationRange LocationRange } var _ ValueIterator = &InclusiveRangeIterator{} func NewInclusiveRangeIterator( interpreter *Interpreter, + locationRange LocationRange, v *CompositeValue, typ InclusiveRangeStaticType, ) *InclusiveRangeIterator { - startValue := getFieldAsIntegerValue(interpreter, EmptyLocationRange, sema.InclusiveRangeTypeStartFieldName) + startValue := getFieldAsIntegerValue(interpreter, v, locationRange, sema.InclusiveRangeTypeStartFieldName) zeroValue := GetSmallIntegerValue(0, typ.ElementType) - endValue := getFieldAsIntegerValue(interpreter, v, EmptyLocationRange, sema.InclusiveRangeTypeEndFieldName) + endValue := getFieldAsIntegerValue(interpreter, v, locationRange, sema.InclusiveRangeTypeEndFieldName) - stepValue := getFieldAsIntegerValue(interpreter, v, EmptyLocationRange, sema.InclusiveRangeTypeStepFieldName) - stepNegative := stepValue.Less(interpreter, zeroValue, EmptyLocationRange) + stepValue := getFieldAsIntegerValue(interpreter, v, locationRange, sema.InclusiveRangeTypeStepFieldName) + stepNegative := stepValue.Less(interpreter, zeroValue, locationRange) return &InclusiveRangeIterator{ rangeValue: v, @@ -17834,14 +17837,14 @@ func (i *InclusiveRangeIterator) Next(interpreter *Interpreter) Value { valueToReturn := i.next // Ensure that valueToReturn is within the bounds. - if i.stepNegative && bool(valueToReturn.Less(interpreter, i.end, EmptyLocationRange)) { + if i.stepNegative && bool(valueToReturn.Less(interpreter, i.end, i.locationRange)) { return nil - } else if !i.stepNegative && bool(valueToReturn.Greater(interpreter, i.end, EmptyLocationRange)) { + } else if !i.stepNegative && bool(valueToReturn.Greater(interpreter, i.end, i.locationRange)) { return nil } // Update the next value. - nextValueToReturn, ok := valueToReturn.Plus(interpreter, i.step, EmptyLocationRange).(IntegerValue) + nextValueToReturn, ok := valueToReturn.Plus(interpreter, i.step, i.locationRange).(IntegerValue) if !ok { panic(errors.NewUnreachableError()) } diff --git a/runtime/tests/interpreter/for_test.go b/runtime/tests/interpreter/for_test.go index 30e751dc9a..436657d827 100644 --- a/runtime/tests/interpreter/for_test.go +++ b/runtime/tests/interpreter/for_test.go @@ -345,7 +345,7 @@ func TestInclusiveRangeForInLoop(t *testing.T) { ) count := 0 - iterator := (loopElements).(*interpreter.ArrayValue).Iterator(inter) + iterator := (loopElements).(*interpreter.ArrayValue).Iterator(inter, interpreter.EmptyLocationRange) for { elem := iterator.Next(inter) if elem == nil { From b512835679ec724dadded298f08ce3fc159d3d78 Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Tue, 7 Nov 2023 18:17:16 +0530 Subject: [PATCH 097/121] Also pass location range in Next --- runtime/interpreter/interpreter_statement.go | 2 +- runtime/interpreter/value.go | 16 +++++++--------- runtime/tests/interpreter/for_test.go | 2 +- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/runtime/interpreter/interpreter_statement.go b/runtime/interpreter/interpreter_statement.go index 60260a4b3e..f2d4016c47 100644 --- a/runtime/interpreter/interpreter_statement.go +++ b/runtime/interpreter/interpreter_statement.go @@ -342,7 +342,7 @@ func (interpreter *Interpreter) VisitForStatement(statement *ast.ForStatement) S } for { - value := iterator.Next(interpreter) + value := iterator.Next(interpreter, locationRange) if value == nil { return nil } diff --git a/runtime/interpreter/value.go b/runtime/interpreter/value.go index c95269c988..80fa00c457 100644 --- a/runtime/interpreter/value.go +++ b/runtime/interpreter/value.go @@ -252,7 +252,7 @@ type IterableValue interface { // ValueIterator is an iterator which returns values. // When Next returns nil, it signals the end of the iterator. type ValueIterator interface { - Next(interpreter *Interpreter) Value + Next(interpreter *Interpreter, locationRange LocationRange) Value } func safeAdd(a, b int, locationRange LocationRange) int { @@ -1528,7 +1528,7 @@ type StringValueIterator struct { var _ ValueIterator = StringValueIterator{} -func (i StringValueIterator) Next(_ *Interpreter) Value { +func (i StringValueIterator) Next(_ *Interpreter, _ LocationRange) Value { if !i.graphemes.Next() { return nil } @@ -1562,7 +1562,7 @@ func (v *ArrayValue) Iterator(_ *Interpreter, _ LocationRange) ValueIterator { var _ ValueIterator = ArrayValueIterator{} -func (i ArrayValueIterator) Next(interpreter *Interpreter) Value { +func (i ArrayValueIterator) Next(interpreter *Interpreter, _ LocationRange) Value { atreeValue, err := i.atreeIterator.Next() if err != nil { panic(errors.NewExternalError(err)) @@ -17804,8 +17804,6 @@ type InclusiveRangeIterator struct { stepNegative bool step IntegerValue end IntegerValue - - locationRange LocationRange } var _ ValueIterator = &InclusiveRangeIterator{} @@ -17833,18 +17831,18 @@ func NewInclusiveRangeIterator( } } -func (i *InclusiveRangeIterator) Next(interpreter *Interpreter) Value { +func (i *InclusiveRangeIterator) Next(interpreter *Interpreter, locationRange LocationRange) Value { valueToReturn := i.next // Ensure that valueToReturn is within the bounds. - if i.stepNegative && bool(valueToReturn.Less(interpreter, i.end, i.locationRange)) { + if i.stepNegative && bool(valueToReturn.Less(interpreter, i.end, locationRange)) { return nil - } else if !i.stepNegative && bool(valueToReturn.Greater(interpreter, i.end, i.locationRange)) { + } else if !i.stepNegative && bool(valueToReturn.Greater(interpreter, i.end, locationRange)) { return nil } // Update the next value. - nextValueToReturn, ok := valueToReturn.Plus(interpreter, i.step, i.locationRange).(IntegerValue) + nextValueToReturn, ok := valueToReturn.Plus(interpreter, i.step, locationRange).(IntegerValue) if !ok { panic(errors.NewUnreachableError()) } diff --git a/runtime/tests/interpreter/for_test.go b/runtime/tests/interpreter/for_test.go index 436657d827..7a59f37398 100644 --- a/runtime/tests/interpreter/for_test.go +++ b/runtime/tests/interpreter/for_test.go @@ -347,7 +347,7 @@ func TestInclusiveRangeForInLoop(t *testing.T) { count := 0 iterator := (loopElements).(*interpreter.ArrayValue).Iterator(inter, interpreter.EmptyLocationRange) for { - elem := iterator.Next(inter) + elem := iterator.Next(inter, interpreter.EmptyLocationRange) if elem == nil { break } From d51c0358ca5049d5aa8f9dbed5649cb5ca2bc6aa Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Fri, 1 Dec 2023 20:45:09 +0530 Subject: [PATCH 098/121] Disallow InclusiveRange if T is non-leaf integer --- runtime/sema/checker.go | 11 +++++- runtime/sema/errors.go | 23 ++++++++++++ runtime/sema/type.go | 37 +++++++++++++++---- runtime/tests/checker/range_value_test.go | 45 +++++++++++++++++++++++ 4 files changed, 108 insertions(+), 8 deletions(-) diff --git a/runtime/sema/checker.go b/runtime/sema/checker.go index fbe9759092..7bbb79ef74 100644 --- a/runtime/sema/checker.go +++ b/runtime/sema/checker.go @@ -2244,7 +2244,16 @@ func (checker *Checker) convertInstantiationType(t *ast.InstantiationType) Type return ty } - return parameterizedType.Instantiate(typeArguments, checker.report) + astRange := ast.NewRange( + checker.memoryGauge, + t.TypeArgumentsStartPos, + t.EndPos, + ) + return parameterizedType.Instantiate( + typeArguments, + checker.report, + &astRange, + ) } func (checker *Checker) VisitExpression(expr ast.Expression, expectedType Type) Type { diff --git a/runtime/sema/errors.go b/runtime/sema/errors.go index 20cbb9a114..54cbdcaa2d 100644 --- a/runtime/sema/errors.go +++ b/runtime/sema/errors.go @@ -3735,6 +3735,29 @@ func (e *MissingTypeArgumentError) Error() string { return fmt.Sprintf("non-optional type argument %s missing", e.TypeArgumentName) } +// InvalidTypeArgumentError + +type InvalidTypeArgumentError struct { + TypeArgumentName string + Details string + ast.Range +} + +var _ SemanticError = &InvalidTypeArgumentError{} +var _ errors.UserError = &InvalidTypeArgumentError{} + +func (e *InvalidTypeArgumentError) isSemanticError() {} + +func (*InvalidTypeArgumentError) IsUserError() {} + +func (e *InvalidTypeArgumentError) Error() string { + return fmt.Sprintf("type argument %s invalid", e.TypeArgumentName) +} + +func (e *InvalidTypeArgumentError) SecondaryError() string { + return e.Details +} + // TypeParameterTypeInferenceError type TypeParameterTypeInferenceError struct { diff --git a/runtime/sema/type.go b/runtime/sema/type.go index 4fe8cb6400..cddfb82f53 100644 --- a/runtime/sema/type.go +++ b/runtime/sema/type.go @@ -277,7 +277,7 @@ type LocatedType interface { type ParameterizedType interface { Type TypeParameters() []*TypeParameter - Instantiate(typeArguments []Type, report func(err error)) Type + Instantiate(typeArguments []Type, report func(err error), astRange *ast.Range) Type BaseType() Type TypeArguments() []Type } @@ -288,6 +288,7 @@ func MustInstantiate(t ParameterizedType, typeArguments ...Type) Type { func(err error) { panic(errors.NewUnexpectedErrorFromCause(err)) }, + nil, ) } @@ -3497,13 +3498,15 @@ var AllUnsignedIntegerTypes = []Type{ Word256Type, } +var AllNonLeafIntegerTypes = []Type{ + IntegerType, + SignedIntegerType, +} + var AllIntegerTypes = common.Concat( AllUnsignedIntegerTypes, AllSignedIntegerTypes, - []Type{ - IntegerType, - SignedIntegerType, - }, + AllNonLeafIntegerTypes, ) var AllNumberTypes = common.Concat( @@ -5399,8 +5402,28 @@ func (t *InclusiveRangeType) BaseType() Type { return &InclusiveRangeType{} } -func (t *InclusiveRangeType) Instantiate(typeArguments []Type, report func(err error)) Type { +func (t *InclusiveRangeType) Instantiate( + typeArguments []Type, + report func(err error), + astRange *ast.Range, +) Type { memberType := typeArguments[0] + + if astRange == nil { + panic(errors.NewUnreachableError()) + } + + // memberType must only be a leaf integer type. + for _, ty := range AllNonLeafIntegerTypes { + if memberType == ty { + report(&InvalidTypeArgumentError{ + TypeArgumentName: inclusiveRangeTypeParameter.Name, + Range: *astRange, + Details: fmt.Sprintf("Creation of InclusiveRange<%s> is disallowed", memberType), + }) + } + } + return &InclusiveRangeType{ MemberType: memberType, } @@ -7258,7 +7281,7 @@ func (t *CapabilityType) TypeParameters() []*TypeParameter { } } -func (t *CapabilityType) Instantiate(typeArguments []Type, _ func(err error)) Type { +func (t *CapabilityType) Instantiate(typeArguments []Type, _ func(err error), _ *ast.Range) Type { borrowType := typeArguments[0] return &CapabilityType{ BorrowType: borrowType, diff --git a/runtime/tests/checker/range_value_test.go b/runtime/tests/checker/range_value_test.go index 196f3baf25..ef3829fbfd 100644 --- a/runtime/tests/checker/range_value_test.go +++ b/runtime/tests/checker/range_value_test.go @@ -377,3 +377,48 @@ func TestCheckInclusiveRangeConstructionInvalid(t *testing.T) { []error{&sema.MissingTypeArgumentError{}}, ) } + +func TestInclusiveRangeNonLeafIntegerTypes(t *testing.T) { + + t.Parallel() + + baseValueActivation := sema.NewVariableActivation(sema.BaseValueActivation) + baseValueActivation.DeclareValue(stdlib.InclusiveRangeConstructorFunction) + + options := ParseAndCheckOptions{ + Config: &sema.Config{ + BaseValueActivation: baseValueActivation, + }, + } + + test := func(t *testing.T, ty sema.Type) { + t.Run(fmt.Sprintf("InclusiveRange<%s>", ty), func(t *testing.T) { + t.Parallel() + + _, err := ParseAndCheckWithOptions(t, fmt.Sprintf(` + let a: %[1]s = 0 + let b: %[1]s = 10 + var range: InclusiveRange<%[1]s> = InclusiveRange<%[1]s>(a, b) + `, ty), options) + + errs := RequireCheckerErrors(t, err, 1) + assert.IsType(t, &sema.InvalidTypeArgumentError{}, errs[0]) + }) + + t.Run(fmt.Sprintf("InclusiveRange<%s> assignment", ty), func(t *testing.T) { + t.Parallel() + + _, err := ParseAndCheckWithOptions(t, fmt.Sprintf(` + let a: InclusiveRange = InclusiveRange(0, 10) + let b: InclusiveRange<%s> = a + `, ty), options) + + errs := RequireCheckerErrors(t, err, 1) + assert.IsType(t, &sema.InvalidTypeArgumentError{}, errs[0]) + }) + } + + for _, ty := range sema.AllNonLeafIntegerTypes { + test(t, ty) + } +} From f0c20a7d24f9418cf8261eb4b6bd32b2512eccdd Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Fri, 1 Dec 2023 21:48:43 +0530 Subject: [PATCH 099/121] Fix tests --- runtime/program_params_validation_test.go | 16 +++-- runtime/tests/checker/for_test.go | 6 ++ .../tests/interpreter/dynamic_casting_test.go | 66 +------------------ 3 files changed, 19 insertions(+), 69 deletions(-) diff --git a/runtime/program_params_validation_test.go b/runtime/program_params_validation_test.go index 9f33f24648..2da4ddefa6 100644 --- a/runtime/program_params_validation_test.go +++ b/runtime/program_params_validation_test.go @@ -29,6 +29,7 @@ import ( "github.com/onflow/cadence/encoding/json" "github.com/onflow/cadence/runtime/common" "github.com/onflow/cadence/runtime/sema" + "github.com/onflow/cadence/runtime/tests/checker" . "github.com/onflow/cadence/runtime/tests/utils" ) @@ -323,7 +324,6 @@ func TestRuntimeScriptParameterTypeValidation(t *testing.T) { assert.NoError(t, err) }) - // Since InclusiveRange isn't covariant. t.Run("Invalid InclusiveRange", func(t *testing.T) { t.Parallel() @@ -338,8 +338,11 @@ func TestRuntimeScriptParameterTypeValidation(t *testing.T) { cadence.NewInclusiveRange(cadence.NewInt16(1), cadence.NewInt16(2), cadence.NewInt16(1)), ) - var entryPointErr *InvalidEntryPointArgumentError - require.ErrorAs(t, err, &entryPointErr) + var checkerError *sema.CheckerError + require.ErrorAs(t, err, &checkerError) + + errs := checker.RequireCheckerErrors(t, checkerError, 1) + assert.IsType(t, &sema.InvalidTypeArgumentError{}, errs[0]) }) t.Run("Invalid InclusiveRange with mixed value types", func(t *testing.T) { @@ -374,8 +377,11 @@ func TestRuntimeScriptParameterTypeValidation(t *testing.T) { cadence.NewInclusiveRange(cadence.NewInt16(1), cadence.NewUInt(2), cadence.NewUInt(1)), ) - var entryPointErr *InvalidEntryPointArgumentError - require.ErrorAs(t, err, &entryPointErr) + var checkerError *sema.CheckerError + require.ErrorAs(t, err, &checkerError) + + errs := checker.RequireCheckerErrors(t, checkerError, 1) + assert.IsType(t, &sema.InvalidTypeArgumentError{}, errs[0]) }) t.Run("Capability", func(t *testing.T) { diff --git a/runtime/tests/checker/for_test.go b/runtime/tests/checker/for_test.go index 5746d2d586..167c4150ff 100644 --- a/runtime/tests/checker/for_test.go +++ b/runtime/tests/checker/for_test.go @@ -86,6 +86,12 @@ func TestCheckForInclusiveRange(t *testing.T) { baseValueActivation.DeclareValue(stdlib.InclusiveRangeConstructorFunction) for _, typ := range sema.AllIntegerTypes { + // Only test leaf integer types + switch typ { + case sema.IntegerType, sema.SignedIntegerType: + continue + } + code := fmt.Sprintf(` fun test() { let start : %[1]s = 1 diff --git a/runtime/tests/interpreter/dynamic_casting_test.go b/runtime/tests/interpreter/dynamic_casting_test.go index 71cd10af7e..b1c4929ab9 100644 --- a/runtime/tests/interpreter/dynamic_casting_test.go +++ b/runtime/tests/interpreter/dynamic_casting_test.go @@ -1471,15 +1471,6 @@ func TestInterpretDynamicCastingInclusiveRange(t *testing.T) { t.Parallel() - types := []sema.Type{ - &sema.InclusiveRangeType{ - MemberType: sema.IntType, - }, - &sema.InclusiveRangeType{ - MemberType: sema.IntegerType, - }, - } - baseValueActivation := sema.NewVariableActivation(sema.BaseValueActivation) baseValueActivation.DeclareValue(stdlib.InclusiveRangeConstructorFunction) @@ -1498,66 +1489,13 @@ func TestInterpretDynamicCastingInclusiveRange(t *testing.T) { for operation, returnsOptional := range dynamicCastingOperations { t.Run(operation.Symbol(), func(t *testing.T) { - - for _, fromType := range types { - for _, targetType := range types { - - t.Run(fmt.Sprintf("valid: from %s to %s", fromType, targetType), func(t *testing.T) { - - inter, err := parseCheckAndInterpretWithOptions(t, - fmt.Sprintf( - ` - let x: InclusiveRange = InclusiveRange(10, 20) - let y: %[1]s = x - let z: %[2]s? = y %[3]s %[2]s - `, - fromType, - targetType, - operation.Symbol(), - ), - options, - ) - require.NoError(t, err) - - expectedInclusiveRange := interpreter.NewInclusiveRangeValue( - inter, - interpreter.EmptyLocationRange, - interpreter.NewUnmeteredIntValueFromInt64(10), - interpreter.NewUnmeteredIntValueFromInt64(20), - interpreter.InclusiveRangeStaticType{ - ElementType: interpreter.PrimitiveStaticTypeInt, - }, - sema.NewInclusiveRangeType(nil, sema.IntType), - ) - - AssertValuesEqual( - t, - inter, - expectedInclusiveRange, - inter.Globals.Get("y").GetValue(), - ) - - AssertValuesEqual( - t, - inter, - interpreter.NewUnmeteredSomeValueNonCopying( - expectedInclusiveRange, - ), - inter.Globals.Get("z").GetValue(), - ) - }) - } - - // We cannot test for invalid casts for T since InclusiveRange has a type bound Integer on T. - } - - t.Run("invalid upcast", func(t *testing.T) { + t.Run("invalid cast", func(t *testing.T) { inter, err := parseCheckAndInterpretWithOptions(t, fmt.Sprintf( ` fun test(): InclusiveRange? { - let x: InclusiveRange = InclusiveRange(10, 20) + let x: InclusiveRange = InclusiveRange(10, 20) return x %s InclusiveRange } `, From ecd3f234f4e8f1fe2ea45a467f93bdc14cc26bb9 Mon Sep 17 00:00:00 2001 From: darkdrag00nv2 <122124396+darkdrag00nv2@users.noreply.github.com> Date: Sat, 2 Dec 2023 11:44:04 +0530 Subject: [PATCH 100/121] Remove unused variable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bastian Müller --- runtime/sema/errors.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/sema/errors.go b/runtime/sema/errors.go index 54cbdcaa2d..6baa7e0b8f 100644 --- a/runtime/sema/errors.go +++ b/runtime/sema/errors.go @@ -3746,7 +3746,7 @@ type InvalidTypeArgumentError struct { var _ SemanticError = &InvalidTypeArgumentError{} var _ errors.UserError = &InvalidTypeArgumentError{} -func (e *InvalidTypeArgumentError) isSemanticError() {} +func (InvalidTypeArgumentError) isSemanticError() {} func (*InvalidTypeArgumentError) IsUserError() {} From 7b65121e7375d2a7c3e763ae3562557e1bf74354 Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Sat, 2 Dec 2023 12:59:31 +0530 Subject: [PATCH 101/121] Add ability to specify custom type parameter validation --- runtime/sema/check_invocation_expression.go | 15 +++++++++++++++ runtime/sema/errors.go | 2 +- runtime/sema/type.go | 7 +++++++ runtime/stdlib/range.go | 19 +++++++++++++++++++ runtime/tests/checker/range_value_test.go | 17 ++++++++++++++++- 5 files changed, 58 insertions(+), 2 deletions(-) diff --git a/runtime/sema/check_invocation_expression.go b/runtime/sema/check_invocation_expression.go index a1ffe69b42..ea6f404c2d 100644 --- a/runtime/sema/check_invocation_expression.go +++ b/runtime/sema/check_invocation_expression.go @@ -493,6 +493,21 @@ func (checker *Checker) checkInvocation( invocationExpression, ) + // The invokable type might have special checks for the type parameters. + + if functionType.TypePrametersCheck != nil { + invocationRange := ast.NewRangeFromPositioned( + checker.memoryGauge, + invocationExpression, + ) + + functionType.TypePrametersCheck( + typeArguments, + checker.report, + invocationRange, + ) + } + // Save types in the elaboration checker.Elaboration.SetInvocationExpressionTypes( diff --git a/runtime/sema/errors.go b/runtime/sema/errors.go index 6baa7e0b8f..3e5c8d9676 100644 --- a/runtime/sema/errors.go +++ b/runtime/sema/errors.go @@ -3746,7 +3746,7 @@ type InvalidTypeArgumentError struct { var _ SemanticError = &InvalidTypeArgumentError{} var _ errors.UserError = &InvalidTypeArgumentError{} -func (InvalidTypeArgumentError) isSemanticError() {} +func (*InvalidTypeArgumentError) isSemanticError() {} func (*InvalidTypeArgumentError) IsUserError() {} diff --git a/runtime/sema/type.go b/runtime/sema/type.go index cddfb82f53..505835b278 100644 --- a/runtime/sema/type.go +++ b/runtime/sema/type.go @@ -2906,6 +2906,7 @@ type FunctionType struct { ReturnTypeAnnotation TypeAnnotation Arity *Arity ArgumentExpressionsCheck ArgumentExpressionsCheck + TypePrametersCheck TypeParametersCheck Members *StringMemberOrderedMap TypeParameters []*TypeParameter Parameters []Parameter @@ -3376,6 +3377,12 @@ type ArgumentExpressionsCheck func( invocationRange ast.Range, ) +type TypeParametersCheck func( + typeArguments *TypeParameterTypeOrderedMap, + report func(err error), + invocationRange ast.Range, +) + // BaseTypeActivation is the base activation that contains // the types available in programs var BaseTypeActivation = NewVariableActivation(nil) diff --git a/runtime/stdlib/range.go b/runtime/stdlib/range.go index 4f2f54227d..d15f12abe2 100644 --- a/runtime/stdlib/range.go +++ b/runtime/stdlib/range.go @@ -21,6 +21,7 @@ package stdlib import ( "fmt" + "github.com/onflow/cadence/runtime/ast" "github.com/onflow/cadence/runtime/errors" "github.com/onflow/cadence/runtime/interpreter" "github.com/onflow/cadence/runtime/sema" @@ -74,6 +75,24 @@ var inclusiveRangeConstructorFunctionType = func() *sema.FunctionType { ), // `step` parameter is optional Arity: &sema.Arity{Min: 2, Max: 3}, + TypePrametersCheck: func(typeArguments *sema.TypeParameterTypeOrderedMap, report func(error), invocationRange ast.Range) { + memberType, ok := typeArguments.Get(typeParameter) + if !ok || memberType == nil { + // checker should prevent this + panic(errors.NewUnreachableError()) + } + + // memberType must only be a leaf integer type. + for _, ty := range sema.AllNonLeafIntegerTypes { + if memberType == ty { + report(&sema.InvalidTypeArgumentError{ + TypeArgumentName: typeParameter.Name, + Range: invocationRange, + Details: fmt.Sprintf("Creation of InclusiveRange<%s> is disallowed", memberType), + }) + } + } + }, } }() diff --git a/runtime/tests/checker/range_value_test.go b/runtime/tests/checker/range_value_test.go index ef3829fbfd..9c851ecf2f 100644 --- a/runtime/tests/checker/range_value_test.go +++ b/runtime/tests/checker/range_value_test.go @@ -398,13 +398,28 @@ func TestInclusiveRangeNonLeafIntegerTypes(t *testing.T) { _, err := ParseAndCheckWithOptions(t, fmt.Sprintf(` let a: %[1]s = 0 let b: %[1]s = 10 - var range: InclusiveRange<%[1]s> = InclusiveRange<%[1]s>(a, b) + var range = InclusiveRange<%[1]s>(a, b) `, ty), options) errs := RequireCheckerErrors(t, err, 1) assert.IsType(t, &sema.InvalidTypeArgumentError{}, errs[0]) }) + t.Run(fmt.Sprintf("InclusiveRange<%s>", ty), func(t *testing.T) { + t.Parallel() + + _, err := ParseAndCheckWithOptions(t, fmt.Sprintf(` + let a: %[1]s = 0 + let b: %[1]s = 10 + var range: InclusiveRange<%[1]s> = InclusiveRange<%[1]s>(a, b) + `, ty), options) + + // One for the invocation and another for the type. + errs := RequireCheckerErrors(t, err, 2) + assert.IsType(t, &sema.InvalidTypeArgumentError{}, errs[0]) + assert.IsType(t, &sema.InvalidTypeArgumentError{}, errs[1]) + }) + t.Run(fmt.Sprintf("InclusiveRange<%s> assignment", ty), func(t *testing.T) { t.Parallel() From eefb4a1ca3d431f9b4cd63b2ee65ec66d7f2b8ea Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Sat, 2 Dec 2023 13:27:05 +0530 Subject: [PATCH 102/121] pass AST type argument range in Instantiate, also fix tests --- runtime/sema/checker.go | 8 ++----- runtime/sema/type.go | 20 +++++++++++----- runtime/tests/checker/range_value_test.go | 22 +++++++++++++++++ runtime/tests/interpreter/range_value_test.go | 24 ------------------- 4 files changed, 38 insertions(+), 36 deletions(-) diff --git a/runtime/sema/checker.go b/runtime/sema/checker.go index 7bbb79ef74..45fe760098 100644 --- a/runtime/sema/checker.go +++ b/runtime/sema/checker.go @@ -2244,15 +2244,11 @@ func (checker *Checker) convertInstantiationType(t *ast.InstantiationType) Type return ty } - astRange := ast.NewRange( - checker.memoryGauge, - t.TypeArgumentsStartPos, - t.EndPos, - ) return parameterizedType.Instantiate( + checker.memoryGauge, typeArguments, + t.TypeArguments, checker.report, - &astRange, ) } diff --git a/runtime/sema/type.go b/runtime/sema/type.go index 505835b278..31b64ddb38 100644 --- a/runtime/sema/type.go +++ b/runtime/sema/type.go @@ -277,18 +277,19 @@ type LocatedType interface { type ParameterizedType interface { Type TypeParameters() []*TypeParameter - Instantiate(typeArguments []Type, report func(err error), astRange *ast.Range) Type + Instantiate(memoryGauge common.MemoryGauge, typeArguments []Type, astTypeArguments []*ast.TypeAnnotation, report func(err error)) Type BaseType() Type TypeArguments() []Type } func MustInstantiate(t ParameterizedType, typeArguments ...Type) Type { return t.Instantiate( + nil, /* memoryGauge */ typeArguments, + nil, /* astTypeArguments */ func(err error) { panic(errors.NewUnexpectedErrorFromCause(err)) }, - nil, ) } @@ -5410,22 +5411,24 @@ func (t *InclusiveRangeType) BaseType() Type { } func (t *InclusiveRangeType) Instantiate( + memoryGauge common.MemoryGauge, typeArguments []Type, + astTypeArguments []*ast.TypeAnnotation, report func(err error), - astRange *ast.Range, ) Type { memberType := typeArguments[0] - if astRange == nil { + if astTypeArguments == nil || astTypeArguments[0] == nil { panic(errors.NewUnreachableError()) } + paramAstRange := ast.NewRangeFromPositioned(memoryGauge, astTypeArguments[0]) // memberType must only be a leaf integer type. for _, ty := range AllNonLeafIntegerTypes { if memberType == ty { report(&InvalidTypeArgumentError{ TypeArgumentName: inclusiveRangeTypeParameter.Name, - Range: *astRange, + Range: paramAstRange, Details: fmt.Sprintf("Creation of InclusiveRange<%s> is disallowed", memberType), }) } @@ -7288,7 +7291,12 @@ func (t *CapabilityType) TypeParameters() []*TypeParameter { } } -func (t *CapabilityType) Instantiate(typeArguments []Type, _ func(err error), _ *ast.Range) Type { +func (t *CapabilityType) Instantiate( + memoryGauge common.MemoryGauge, + typeArguments []Type, + _ []*ast.TypeAnnotation, + _ func(err error), +) Type { borrowType := typeArguments[0] return &CapabilityType{ BorrowType: borrowType, diff --git a/runtime/tests/checker/range_value_test.go b/runtime/tests/checker/range_value_test.go index 9c851ecf2f..9907de2742 100644 --- a/runtime/tests/checker/range_value_test.go +++ b/runtime/tests/checker/range_value_test.go @@ -376,6 +376,28 @@ func TestCheckInclusiveRangeConstructionInvalid(t *testing.T) { "let r: InclusiveRange = InclusiveRange(1, 10)", []error{&sema.MissingTypeArgumentError{}}, ) + + runInvalidCase( + t, + "same_supertype_different_subtype_start_end", + ` + let a: Integer = UInt8(0) + let b: Integer = Int16(10) + let r = InclusiveRange(a, b) + `, + []error{&sema.InvalidTypeArgumentError{}}, + ) + runInvalidCase( + t, + "same_supertype_different_subtype_start_step", + ` + let a: Integer = UInt8(0) + let b: Integer = UInt8(10) + let s: Integer = UInt16(2) + let r = InclusiveRange(a, b, step: s) + `, + []error{&sema.InvalidTypeArgumentError{}}, + ) } func TestInclusiveRangeNonLeafIntegerTypes(t *testing.T) { diff --git a/runtime/tests/interpreter/range_value_test.go b/runtime/tests/interpreter/range_value_test.go index de1ba55c32..8f26b2fa15 100644 --- a/runtime/tests/interpreter/range_value_test.go +++ b/runtime/tests/interpreter/range_value_test.go @@ -586,28 +586,4 @@ func TestInclusiveRangeConstructionInvalid(t *testing.T) { "step value cannot be negative for unsigned integer type", ) } - - runInvalidCase( - t, - "same_supertype_different_subtype_start_end", - ` - let a: Integer = UInt8(0) - let b: Integer = Int16(10) - let r = InclusiveRange(a, b) - `, - &interpreter.InclusiveRangeConstructionError{}, - "start and end are of different types", - ) - runInvalidCase( - t, - "same_supertype_different_subtype_start_step", - ` - let a: Integer = UInt8(0) - let b: Integer = UInt8(10) - let s: Integer = UInt16(2) - let r = InclusiveRange(a, b, step: s) - `, - &interpreter.InclusiveRangeConstructionError{}, - "step must be of the same type as start and end", - ) } From 733cf470680acdc4c1b9cbc763d068fd036cb5d4 Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Sat, 2 Dec 2023 13:41:51 +0530 Subject: [PATCH 103/121] use type argument range in constructor when available --- runtime/sema/check_invocation_expression.go | 19 ++++++++----------- runtime/sema/type.go | 4 +++- runtime/stdlib/range.go | 17 +++++++++++++++-- 3 files changed, 26 insertions(+), 14 deletions(-) diff --git a/runtime/sema/check_invocation_expression.go b/runtime/sema/check_invocation_expression.go index ea6f404c2d..3588856f81 100644 --- a/runtime/sema/check_invocation_expression.go +++ b/runtime/sema/check_invocation_expression.go @@ -459,6 +459,11 @@ func (checker *Checker) checkInvocation( } } + invocationRange := ast.NewRangeFromPositioned( + checker.memoryGauge, + invocationExpression, + ) + // The invokable type might have special checks for the arguments if functionType.ArgumentExpressionsCheck != nil && argumentCount > 0 { @@ -467,11 +472,6 @@ func (checker *Checker) checkInvocation( argumentExpressions[i] = argument.Expression } - invocationRange := ast.NewRangeFromPositioned( - checker.memoryGauge, - invocationExpression, - ) - functionType.ArgumentExpressionsCheck( checker, argumentExpressions, @@ -496,15 +496,12 @@ func (checker *Checker) checkInvocation( // The invokable type might have special checks for the type parameters. if functionType.TypePrametersCheck != nil { - invocationRange := ast.NewRangeFromPositioned( - checker.memoryGauge, - invocationExpression, - ) - functionType.TypePrametersCheck( + checker.memoryGauge, typeArguments, - checker.report, + invocationExpression.TypeArguments, invocationRange, + checker.report, ) } diff --git a/runtime/sema/type.go b/runtime/sema/type.go index 31b64ddb38..f1d5e18f23 100644 --- a/runtime/sema/type.go +++ b/runtime/sema/type.go @@ -3379,9 +3379,11 @@ type ArgumentExpressionsCheck func( ) type TypeParametersCheck func( + memoryGauge common.MemoryGauge, typeArguments *TypeParameterTypeOrderedMap, + astTypeArguments []*ast.TypeAnnotation, + astInvocationRange ast.Range, report func(err error), - invocationRange ast.Range, ) // BaseTypeActivation is the base activation that contains diff --git a/runtime/stdlib/range.go b/runtime/stdlib/range.go index d15f12abe2..84132859a6 100644 --- a/runtime/stdlib/range.go +++ b/runtime/stdlib/range.go @@ -22,6 +22,7 @@ import ( "fmt" "github.com/onflow/cadence/runtime/ast" + "github.com/onflow/cadence/runtime/common" "github.com/onflow/cadence/runtime/errors" "github.com/onflow/cadence/runtime/interpreter" "github.com/onflow/cadence/runtime/sema" @@ -75,19 +76,31 @@ var inclusiveRangeConstructorFunctionType = func() *sema.FunctionType { ), // `step` parameter is optional Arity: &sema.Arity{Min: 2, Max: 3}, - TypePrametersCheck: func(typeArguments *sema.TypeParameterTypeOrderedMap, report func(error), invocationRange ast.Range) { + TypePrametersCheck: func( + memoryGauge common.MemoryGauge, + typeArguments *sema.TypeParameterTypeOrderedMap, + astTypeArguments []*ast.TypeAnnotation, + astInvocationRange ast.Range, + report func(error), + ) { memberType, ok := typeArguments.Get(typeParameter) if !ok || memberType == nil { // checker should prevent this panic(errors.NewUnreachableError()) } + paramAstRange := astInvocationRange + // If type argument was provided, use its range otherwise fallback to invocation range. + if len(astTypeArguments) > 0 { + paramAstRange = ast.NewRangeFromPositioned(memoryGauge, astTypeArguments[0]) + } + // memberType must only be a leaf integer type. for _, ty := range sema.AllNonLeafIntegerTypes { if memberType == ty { report(&sema.InvalidTypeArgumentError{ TypeArgumentName: typeParameter.Name, - Range: invocationRange, + Range: paramAstRange, Details: fmt.Sprintf("Creation of InclusiveRange<%s> is disallowed", memberType), }) } From 2a26bb3faefa59a2d6d839b76cb2c437ac3a11be Mon Sep 17 00:00:00 2001 From: darkdrag00nv2 <122124396+darkdrag00nv2@users.noreply.github.com> Date: Tue, 5 Dec 2023 21:24:19 +0530 Subject: [PATCH 104/121] Computate invocation range lazily MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bastian Müller --- runtime/sema/check_invocation_expression.go | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/runtime/sema/check_invocation_expression.go b/runtime/sema/check_invocation_expression.go index 3588856f81..891f1b5a54 100644 --- a/runtime/sema/check_invocation_expression.go +++ b/runtime/sema/check_invocation_expression.go @@ -459,10 +459,19 @@ func (checker *Checker) checkInvocation( } } - invocationRange := ast.NewRangeFromPositioned( - checker.memoryGauge, - invocationExpression, - ) + // Compute the invocation range, once, if needed + getInvocationRange := func() func() ast.Range { + var invcocationRange ast.Range + return func() ast.Range { + if invocationRange == ast.EmptyRange { + invocationRange = ast.NewRangeFromPositioned( + checker.memoryGauge, + invocationExpression, + ) + } + return invocationRange + } + }() // The invokable type might have special checks for the arguments From fa16545664d40d3576821a3c45c24794ee6059c8 Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Tue, 5 Dec 2023 21:25:33 +0530 Subject: [PATCH 105/121] Fix build --- runtime/sema/check_invocation_expression.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/runtime/sema/check_invocation_expression.go b/runtime/sema/check_invocation_expression.go index 891f1b5a54..d14550b6eb 100644 --- a/runtime/sema/check_invocation_expression.go +++ b/runtime/sema/check_invocation_expression.go @@ -461,7 +461,7 @@ func (checker *Checker) checkInvocation( // Compute the invocation range, once, if needed getInvocationRange := func() func() ast.Range { - var invcocationRange ast.Range + var invocationRange ast.Range return func() ast.Range { if invocationRange == ast.EmptyRange { invocationRange = ast.NewRangeFromPositioned( @@ -484,7 +484,7 @@ func (checker *Checker) checkInvocation( functionType.ArgumentExpressionsCheck( checker, argumentExpressions, - invocationRange, + getInvocationRange(), ) } @@ -509,7 +509,7 @@ func (checker *Checker) checkInvocation( checker.memoryGauge, typeArguments, invocationExpression.TypeArguments, - invocationRange, + getInvocationRange(), checker.report, ) } From d5b746c1940085ee2b4293a0163f8b3ad6779fa4 Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Tue, 5 Dec 2023 21:27:52 +0530 Subject: [PATCH 106/121] Rename to TypeArgumentsCheck --- runtime/sema/check_invocation_expression.go | 4 ++-- runtime/sema/type.go | 4 ++-- runtime/stdlib/range.go | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/runtime/sema/check_invocation_expression.go b/runtime/sema/check_invocation_expression.go index d14550b6eb..cd8c381b37 100644 --- a/runtime/sema/check_invocation_expression.go +++ b/runtime/sema/check_invocation_expression.go @@ -504,8 +504,8 @@ func (checker *Checker) checkInvocation( // The invokable type might have special checks for the type parameters. - if functionType.TypePrametersCheck != nil { - functionType.TypePrametersCheck( + if functionType.TypeArgumentsCheck != nil { + functionType.TypeArgumentsCheck( checker.memoryGauge, typeArguments, invocationExpression.TypeArguments, diff --git a/runtime/sema/type.go b/runtime/sema/type.go index f1d5e18f23..20db7a64cc 100644 --- a/runtime/sema/type.go +++ b/runtime/sema/type.go @@ -2907,7 +2907,7 @@ type FunctionType struct { ReturnTypeAnnotation TypeAnnotation Arity *Arity ArgumentExpressionsCheck ArgumentExpressionsCheck - TypePrametersCheck TypeParametersCheck + TypeArgumentsCheck TypeArgumentsCheck Members *StringMemberOrderedMap TypeParameters []*TypeParameter Parameters []Parameter @@ -3378,7 +3378,7 @@ type ArgumentExpressionsCheck func( invocationRange ast.Range, ) -type TypeParametersCheck func( +type TypeArgumentsCheck func( memoryGauge common.MemoryGauge, typeArguments *TypeParameterTypeOrderedMap, astTypeArguments []*ast.TypeAnnotation, diff --git a/runtime/stdlib/range.go b/runtime/stdlib/range.go index 84132859a6..9d493ccdaf 100644 --- a/runtime/stdlib/range.go +++ b/runtime/stdlib/range.go @@ -76,7 +76,7 @@ var inclusiveRangeConstructorFunctionType = func() *sema.FunctionType { ), // `step` parameter is optional Arity: &sema.Arity{Min: 2, Max: 3}, - TypePrametersCheck: func( + TypeArgumentsCheck: func( memoryGauge common.MemoryGauge, typeArguments *sema.TypeParameterTypeOrderedMap, astTypeArguments []*ast.TypeAnnotation, From da3dfc238c2dbef3b30dea6fb1bedc86ae1d5bdb Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Tue, 31 Oct 2023 21:31:37 +0530 Subject: [PATCH 107/121] Make checkParameterizedTypeIsInstantiated recursive --- runtime/sema/checker.go | 105 ++++++++++++++++----- runtime/tests/checker/typeargument_test.go | 96 ++++++++++++++----- 2 files changed, 152 insertions(+), 49 deletions(-) diff --git a/runtime/sema/checker.go b/runtime/sema/checker.go index 45fe760098..830b9be728 100644 --- a/runtime/sema/checker.go +++ b/runtime/sema/checker.go @@ -2069,37 +2069,74 @@ func (checker *Checker) checkInvalidInterfaceAsType(ty Type, pos ast.HasPosition } func (checker *Checker) checkParameterizedTypeIsInstantiated(ty Type, pos ast.HasPosition) { - parameterizedType, ok := ty.(ParameterizedType) - if !ok { - return - } + switch t := ty.(type) { + case *OptionalType: + checker.checkParameterizedTypeIsInstantiated(t.Type, pos) - typeArgs := parameterizedType.TypeArguments() - typeParameters := parameterizedType.TypeParameters() + case ArrayType: + checker.checkParameterizedTypeIsInstantiated(t.ElementType(false), pos) - typeArgumentCount := len(typeArgs) - typeParameterCount := len(typeParameters) + case *DictionaryType: + checker.checkParameterizedTypeIsInstantiated(t.KeyType, pos) + checker.checkParameterizedTypeIsInstantiated(t.ValueType, pos) - if typeArgumentCount != typeParameterCount { - checker.report( - &InvalidTypeArgumentCountError{ - TypeParameterCount: typeParameterCount, - TypeArgumentCount: typeArgumentCount, - Range: ast.NewRange( - checker.memoryGauge, - pos.StartPosition(), - pos.EndPosition(checker.memoryGauge), - ), - }, - ) - } + case *ReferenceType: + checker.checkParameterizedTypeIsInstantiated(t.Type, pos) + + case *FunctionType: + for _, tyParam := range t.TypeParameters { + checker.checkParameterizedTypeIsInstantiated(tyParam.TypeBound, pos) + } + + for _, param := range t.Parameters { + checker.checkParameterizedTypeIsInstantiated(param.TypeAnnotation.Type, pos) + } + + checker.checkParameterizedTypeIsInstantiated(t.ReturnTypeAnnotation.Type, pos) - // Ensure that each non-optional typeparameter is non-nil. - for index, typeParam := range typeParameters { - if !typeParam.Optional && typeArgs[index] == nil { + case *RestrictedType: + checker.checkParameterizedTypeIsInstantiated(t.Type, pos) + + case *CompositeType: + if t.EnumRawType != nil { + checker.checkParameterizedTypeIsInstantiated(t.EnumRawType, pos) + } + if t.containerType != nil { + checker.checkParameterizedTypeIsInstantiated(t.containerType, pos) + } + if t.baseType != nil { + checker.checkParameterizedTypeIsInstantiated(t.baseType, pos) + } + + for _, typ := range t.ImplicitTypeRequirementConformances { + checker.checkParameterizedTypeIsInstantiated(typ, pos) + } + + for _, typ := range t.ExplicitInterfaceConformances { + checker.checkParameterizedTypeIsInstantiated(typ, pos) + } + + case *InterfaceType: + if t.containerType != nil { + checker.checkParameterizedTypeIsInstantiated(t.containerType, pos) + } + + for _, param := range t.InitializerParameters { + checker.checkParameterizedTypeIsInstantiated(param.TypeAnnotation.Type, pos) + } + + case ParameterizedType: + typeArgs := t.TypeArguments() + typeParameters := t.TypeParameters() + + typeArgumentCount := len(typeArgs) + typeParameterCount := len(typeParameters) + + if typeArgumentCount != typeParameterCount { checker.report( - &MissingTypeArgumentError{ - TypeArgumentName: typeParam.Name, + &InvalidTypeArgumentCountError{ + TypeParameterCount: typeParameterCount, + TypeArgumentCount: typeArgumentCount, Range: ast.NewRange( checker.memoryGauge, pos.StartPosition(), @@ -2108,6 +2145,22 @@ func (checker *Checker) checkParameterizedTypeIsInstantiated(ty Type, pos ast.Ha }, ) } + + // Ensure that each non-optional typeparameter is non-nil. + for index, typeParam := range typeParameters { + if !typeParam.Optional && typeArgs[index] == nil { + checker.report( + &MissingTypeArgumentError{ + TypeArgumentName: typeParam.Name, + Range: ast.NewRange( + checker.memoryGauge, + pos.StartPosition(), + pos.EndPosition(checker.memoryGauge), + ), + }, + ) + } + } } } diff --git a/runtime/tests/checker/typeargument_test.go b/runtime/tests/checker/typeargument_test.go index 7167facadf..7cc81f2669 100644 --- a/runtime/tests/checker/typeargument_test.go +++ b/runtime/tests/checker/typeargument_test.go @@ -54,29 +54,6 @@ func TestCheckTypeArguments(t *testing.T) { require.Nil(t, capType.(*sema.CapabilityType).BorrowType) }) - t.Run("inclusive range, no instantiation", func(t *testing.T) { - - t.Parallel() - - baseValueActivation := sema.NewVariableActivation(sema.BaseValueActivation) - baseValueActivation.DeclareValue(stdlib.InclusiveRangeConstructorFunction) - - _, err := ParseAndCheckWithOptions(t, - ` - let inclusiveRange: InclusiveRange = InclusiveRange(1, 10) - `, - ParseAndCheckOptions{ - Config: &sema.Config{ - BaseValueActivation: baseValueActivation, - }, - }, - ) - - errs := RequireCheckerErrors(t, err, 1) - - assert.IsType(t, &sema.MissingTypeArgumentError{}, errs[0]) - }) - t.Run("inclusive range, instantiation with more than arguments", func(t *testing.T) { t.Parallel() @@ -178,6 +155,79 @@ func TestCheckTypeArguments(t *testing.T) { }) } +type checkParameterizedTypeIsInstantiatedTestCase struct { + name string + code string +} + +func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { + + t.Parallel() + + baseValueActivation := sema.NewVariableActivation(sema.BaseValueActivation) + baseValueActivation.DeclareValue(stdlib.InclusiveRangeConstructorFunction) + options := ParseAndCheckOptions{ + Config: &sema.Config{ + BaseValueActivation: baseValueActivation, + }, + } + + runTestCase := func(t *testing.T, testCase checkParameterizedTypeIsInstantiatedTestCase) { + t.Run(testCase.name, func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheckWithOptions(t, + testCase.code, + options, + ) + + errs := RequireCheckerErrors(t, err, 1) + + assert.IsType(t, &sema.MissingTypeArgumentError{}, errs[0]) + }) + } + + testCases := []checkParameterizedTypeIsInstantiatedTestCase{ + { + name: "InclusiveRange", + code: "let inclusiveRange: InclusiveRange = InclusiveRange(1, 10)", + }, + { + name: "VariableSizedArray with InclusiveRange", + code: "let r: [InclusiveRange] = []", + }, + { + name: "ConstantSizedType with InclusiveRange", + code: "let r: [InclusiveRange; 2] = [InclusiveRange(1, 2), InclusiveRange(3, 4)]", + }, + { + name: "OptionalType with InclusiveRange", + code: "let r: InclusiveRange? = nil", + }, + { + name: "DictionaryType with InclusiveRange", + code: "let r: {Int: InclusiveRange} = {}", + }, + { + name: "Struct with InclusiveRange", + code: ` + pub struct Foo { + pub let a: InclusiveRange + + init() { + self.a = InclusiveRange(1, 10) + } + } + `, + }, + } + + for _, test := range testCases { + runTestCase(t, test) + } +} + func TestCheckTypeArgumentSubtyping(t *testing.T) { t.Parallel() From 951a4a7b79ee806bc5167c1f1da40fc6d8a69b9d Mon Sep 17 00:00:00 2001 From: darkdrag00nv2 <122124396+darkdrag00nv2@users.noreply.github.com> Date: Fri, 3 Nov 2023 21:21:41 +0530 Subject: [PATCH 108/121] Remove access modifiers from tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bastian Müller --- runtime/tests/checker/typeargument_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/runtime/tests/checker/typeargument_test.go b/runtime/tests/checker/typeargument_test.go index 7cc81f2669..8699cb54c1 100644 --- a/runtime/tests/checker/typeargument_test.go +++ b/runtime/tests/checker/typeargument_test.go @@ -212,8 +212,8 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { { name: "Struct with InclusiveRange", code: ` - pub struct Foo { - pub let a: InclusiveRange + struct Foo { + let a: InclusiveRange init() { self.a = InclusiveRange(1, 10) From a1fcc7d529e39be525d956fdad6b3ba34407eb6a Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Fri, 3 Nov 2023 21:32:05 +0530 Subject: [PATCH 109/121] Move checkParameterizedTypeIsInstantiatedTestCase inside the test case --- runtime/tests/checker/typeargument_test.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/runtime/tests/checker/typeargument_test.go b/runtime/tests/checker/typeargument_test.go index 8699cb54c1..78eddb3002 100644 --- a/runtime/tests/checker/typeargument_test.go +++ b/runtime/tests/checker/typeargument_test.go @@ -155,11 +155,6 @@ func TestCheckTypeArguments(t *testing.T) { }) } -type checkParameterizedTypeIsInstantiatedTestCase struct { - name string - code string -} - func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { t.Parallel() @@ -172,6 +167,11 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { }, } + type checkParameterizedTypeIsInstantiatedTestCase struct { + name string + code string + } + runTestCase := func(t *testing.T, testCase checkParameterizedTypeIsInstantiatedTestCase) { t.Run(testCase.name, func(t *testing.T) { From 6e05d78b3a63d88b1aee5f2af43145e96c9f2150 Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Sat, 4 Nov 2023 15:28:34 +0530 Subject: [PATCH 110/121] Add more test cases for structs, resources, contracts, enums --- runtime/sema/checker.go | 7 - runtime/tests/checker/typeargument_test.go | 614 +++++++++++++++++++-- 2 files changed, 570 insertions(+), 51 deletions(-) diff --git a/runtime/sema/checker.go b/runtime/sema/checker.go index 830b9be728..8d3d2e1ac9 100644 --- a/runtime/sema/checker.go +++ b/runtime/sema/checker.go @@ -2101,9 +2101,6 @@ func (checker *Checker) checkParameterizedTypeIsInstantiated(ty Type, pos ast.Ha if t.EnumRawType != nil { checker.checkParameterizedTypeIsInstantiated(t.EnumRawType, pos) } - if t.containerType != nil { - checker.checkParameterizedTypeIsInstantiated(t.containerType, pos) - } if t.baseType != nil { checker.checkParameterizedTypeIsInstantiated(t.baseType, pos) } @@ -2117,10 +2114,6 @@ func (checker *Checker) checkParameterizedTypeIsInstantiated(ty Type, pos ast.Ha } case *InterfaceType: - if t.containerType != nil { - checker.checkParameterizedTypeIsInstantiated(t.containerType, pos) - } - for _, param := range t.InitializerParameters { checker.checkParameterizedTypeIsInstantiated(param.TypeAnnotation.Type, pos) } diff --git a/runtime/tests/checker/typeargument_test.go b/runtime/tests/checker/typeargument_test.go index 78eddb3002..25c3044d81 100644 --- a/runtime/tests/checker/typeargument_test.go +++ b/runtime/tests/checker/typeargument_test.go @@ -167,51 +167,162 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { }, } - type checkParameterizedTypeIsInstantiatedTestCase struct { - name string - code string - } + t.Run("InclusiveRange", func(t *testing.T) { - runTestCase := func(t *testing.T, testCase checkParameterizedTypeIsInstantiatedTestCase) { - t.Run(testCase.name, func(t *testing.T) { + t.Parallel() - t.Parallel() + _, err := ParseAndCheckWithOptions(t, + "let inclusiveRange: InclusiveRange = InclusiveRange(1, 10)", + options, + ) - _, err := ParseAndCheckWithOptions(t, - testCase.code, - options, - ) + require.NoError(t, err) + }) - errs := RequireCheckerErrors(t, err, 1) + t.Run("InclusiveRange", func(t *testing.T) { - assert.IsType(t, &sema.MissingTypeArgumentError{}, errs[0]) - }) - } + t.Parallel() - testCases := []checkParameterizedTypeIsInstantiatedTestCase{ - { - name: "InclusiveRange", - code: "let inclusiveRange: InclusiveRange = InclusiveRange(1, 10)", - }, - { - name: "VariableSizedArray with InclusiveRange", - code: "let r: [InclusiveRange] = []", - }, - { - name: "ConstantSizedType with InclusiveRange", - code: "let r: [InclusiveRange; 2] = [InclusiveRange(1, 2), InclusiveRange(3, 4)]", - }, - { - name: "OptionalType with InclusiveRange", - code: "let r: InclusiveRange? = nil", - }, - { - name: "DictionaryType with InclusiveRange", - code: "let r: {Int: InclusiveRange} = {}", - }, - { - name: "Struct with InclusiveRange", - code: ` + _, err := ParseAndCheckWithOptions(t, + "let inclusiveRange: InclusiveRange = InclusiveRange(1, 10)", + options, + ) + + errs := RequireCheckerErrors(t, err, 1) + + assert.IsType(t, &sema.MissingTypeArgumentError{}, errs[0]) + }) + + t.Run("VariableSizedArray with InclusiveRange", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheckWithOptions(t, + "let r: [InclusiveRange] = []", + options, + ) + + require.NoError(t, err) + }) + + t.Run("VariableSizedArray with InclusiveRange", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheckWithOptions(t, + "let r: [InclusiveRange] = []", + options, + ) + + errs := RequireCheckerErrors(t, err, 1) + + assert.IsType(t, &sema.MissingTypeArgumentError{}, errs[0]) + }) + + t.Run("ConstantSizedType with InclusiveRange", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheckWithOptions(t, + "let r: [InclusiveRange; 2] = [InclusiveRange(1, 2), InclusiveRange(3, 4)]", + options, + ) + + require.NoError(t, err) + }) + + t.Run("ConstantSizedType with InclusiveRange", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheckWithOptions(t, + "let r: [InclusiveRange; 2] = [InclusiveRange(1, 2), InclusiveRange(3, 4)]", + options, + ) + + errs := RequireCheckerErrors(t, err, 1) + + assert.IsType(t, &sema.MissingTypeArgumentError{}, errs[0]) + }) + + t.Run("OptionalType with InclusiveRange", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheckWithOptions(t, + "let r: InclusiveRange? = nil", + options, + ) + + require.NoError(t, err) + }) + + t.Run("OptionalType with InclusiveRange", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheckWithOptions(t, + "let r: InclusiveRange? = nil", + options, + ) + + errs := RequireCheckerErrors(t, err, 1) + + assert.IsType(t, &sema.MissingTypeArgumentError{}, errs[0]) + }) + + t.Run("DictionaryType with InclusiveRange", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheckWithOptions(t, + "let r: {Int: InclusiveRange} = {}", + options, + ) + + require.NoError(t, err) + }) + + t.Run("DictionaryType with InclusiveRange", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheckWithOptions(t, + "let r: {Int: InclusiveRange} = {}", + options, + ) + + errs := RequireCheckerErrors(t, err, 1) + + assert.IsType(t, &sema.MissingTypeArgumentError{}, errs[0]) + }) + + t.Run("Struct with InclusiveRange", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheckWithOptions(t, + ` + struct Foo { + let a: InclusiveRange + + init() { + self.a = InclusiveRange(1, 10) + } + } + `, + options, + ) + + require.NoError(t, err) + }) + + t.Run("Struct with InclusiveRange", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheckWithOptions(t, + ` struct Foo { let a: InclusiveRange @@ -220,12 +331,427 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { } } `, - }, - } + options, + ) - for _, test := range testCases { - runTestCase(t, test) - } + errs := RequireCheckerErrors(t, err, 1) + + assert.IsType(t, &sema.MissingTypeArgumentError{}, errs[0]) + }) + + t.Run("Nested Struct with InclusiveRange", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheckWithOptions(t, + ` + struct Bar { + let a: InclusiveRange + + init() { + self.a = InclusiveRange(1, 10) + } + } + + struct Foo { + let bar: Bar + + init(b : Bar) { + self.bar = b + } + } + `, + options, + ) + + require.NoError(t, err) + }) + + t.Run("Nested Struct with InclusiveRange", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheckWithOptions(t, + ` + struct Bar { + let a: InclusiveRange + + init() { + self.a = InclusiveRange(1, 10) + } + } + + struct Foo { + let bar: Bar + + init(b : Bar) { + self.bar = b + } + } + `, + options, + ) + + errs := RequireCheckerErrors(t, err, 1) + + assert.IsType(t, &sema.MissingTypeArgumentError{}, errs[0]) + }) + + t.Run("Contract with Struct with InclusiveRange", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheckWithOptions(t, + ` + contract C { + struct Foo { + let a: InclusiveRange + + init() { + self.a = InclusiveRange(1, 10) + } + } + } + `, + options, + ) + + require.NoError(t, err) + }) + + t.Run("Contract with Struct with InclusiveRange", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheckWithOptions(t, + ` + contract C { + struct Foo { + let a: InclusiveRange + + init() { + self.a = InclusiveRange(1, 10) + } + } + } + `, + options, + ) + + errs := RequireCheckerErrors(t, err, 1) + + assert.IsType(t, &sema.MissingTypeArgumentError{}, errs[0]) + }) + + t.Run("Struct with function returning InclusiveRange", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheckWithOptions(t, + ` + struct Bar { + let f: ((): InclusiveRange) + + init() { + self.f = fun () : InclusiveRange { + return InclusiveRange(1, 10) + } + } + } + `, + options, + ) + + require.NoError(t, err) + }) + + t.Run("Struct with function returning InclusiveRange", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheckWithOptions(t, + ` + struct Bar { + let f: ((): InclusiveRange) + + init() { + self.f = fun () : InclusiveRange { + return InclusiveRange(1, 10) + } + } + } + `, + options, + ) + + errs := RequireCheckerErrors(t, err, 2) + + // 2 errors for the two occurences of InclusiveRange. + assert.IsType(t, &sema.MissingTypeArgumentError{}, errs[0]) + assert.IsType(t, &sema.MissingTypeArgumentError{}, errs[1]) + }) + + t.Run("StructInterface with function returning InclusiveRange", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheckWithOptions(t, + ` + struct interface Bar { + fun getRange(): InclusiveRange + } + `, + options, + ) + + require.NoError(t, err) + }) + + t.Run("StructInterface with function returning InclusiveRange", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheckWithOptions(t, + ` + struct interface Bar { + fun getRange(): InclusiveRange + } + `, + options, + ) + + errs := RequireCheckerErrors(t, err, 1) + + assert.IsType(t, &sema.MissingTypeArgumentError{}, errs[0]) + }) + + t.Run("Resource with InclusiveRange", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheckWithOptions(t, + ` + resource Foo { + let a: InclusiveRange + + init() { + self.a = InclusiveRange(1, 10) + } + } + `, + options, + ) + + require.NoError(t, err) + }) + + t.Run("Resource with InclusiveRange", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheckWithOptions(t, + ` + resource Foo { + let a: InclusiveRange + + init() { + self.a = InclusiveRange(1, 10) + } + } + `, + options, + ) + + errs := RequireCheckerErrors(t, err, 1) + + assert.IsType(t, &sema.MissingTypeArgumentError{}, errs[0]) + }) + + t.Run("Nested Resource with InclusiveRange", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheckWithOptions(t, + ` + resource Bar { + let a: InclusiveRange + + init() { + self.a = InclusiveRange(1, 10) + } + } + + resource Foo { + let bar: @Bar + + init(b : @Bar) { + self.bar <- b + } + + destroy() { + destroy self.bar + } + } + `, + options, + ) + + require.NoError(t, err) + }) + + t.Run("Nested Resource with InclusiveRange", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheckWithOptions(t, + ` + resource Bar { + let a: InclusiveRange + + init() { + self.a = InclusiveRange(1, 10) + } + } + + resource Foo { + let bar: @Bar + + init(b : @Bar) { + self.bar <- b + } + + destroy() { + destroy self.bar + } + } + `, + options, + ) + + errs := RequireCheckerErrors(t, err, 1) + + assert.IsType(t, &sema.MissingTypeArgumentError{}, errs[0]) + }) + + t.Run("ResourceInterface with function returning InclusiveRange", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheckWithOptions(t, + ` + resource interface Bar { + fun getRange(): InclusiveRange + } + `, + options, + ) + + require.NoError(t, err) + }) + + t.Run("ResourceInterface with function returning InclusiveRange", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheckWithOptions(t, + ` + resource interface Bar { + fun getRange(): InclusiveRange + } + `, + options, + ) + + errs := RequireCheckerErrors(t, err, 1) + + assert.IsType(t, &sema.MissingTypeArgumentError{}, errs[0]) + }) + + t.Run("Type with InclusiveRange", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheckWithOptions(t, + ` + pub fun main(): Type { + return Type>() + } + `, + options, + ) + + require.NoError(t, err) + }) + + t.Run("Type with InclusiveRange", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheckWithOptions(t, + ` + pub fun main(): Type { + return Type() + } + `, + options, + ) + + errs := RequireCheckerErrors(t, err, 1) + + assert.IsType(t, &sema.MissingTypeArgumentError{}, errs[0]) + }) + + t.Run("Event with InclusiveRange", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheckWithOptions(t, + "event Foo(bar: InclusiveRange)", + options, + ) + + errs := RequireCheckerErrors(t, err, 1) + + assert.IsType(t, &sema.InvalidEventParameterTypeError{}, errs[0]) + }) + + t.Run("Event with InclusiveRange", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheckWithOptions(t, + "event Foo(bar: InclusiveRange)", + options, + ) + + errs := RequireCheckerErrors(t, err, 2) + + assert.IsType(t, &sema.MissingTypeArgumentError{}, errs[0]) + assert.IsType(t, &sema.InvalidEventParameterTypeError{}, errs[1]) + }) + + t.Run("Enum Declaration", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheckWithOptions(t, + ` + pub fun main(): Direction { + return Direction.RIGHT + } + + pub enum Direction: Int { + pub case UP + pub case DOWN + pub case LEFT + pub case RIGHT + } + `, + options, + ) + + require.NoError(t, err) + }) } func TestCheckTypeArgumentSubtyping(t *testing.T) { From 151816e5a29ab836eb0d331e720807e8a740afd2 Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Sat, 4 Nov 2023 15:34:44 +0530 Subject: [PATCH 111/121] Add test cases for struct/resource interface inside a contract --- runtime/tests/checker/typeargument_test.go | 76 ++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/runtime/tests/checker/typeargument_test.go b/runtime/tests/checker/typeargument_test.go index 25c3044d81..5c6d242edf 100644 --- a/runtime/tests/checker/typeargument_test.go +++ b/runtime/tests/checker/typeargument_test.go @@ -545,6 +545,44 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { require.NoError(t, err) }) + t.Run("Contract with StructInterface with InclusiveRange", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheckWithOptions(t, + ` + contract C { + struct interface Foo { + fun getRange(): InclusiveRange + } + } + `, + options, + ) + + require.NoError(t, err) + }) + + t.Run("Contract with StructInterface with InclusiveRange", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheckWithOptions(t, + ` + contract C { + struct interface Foo { + fun getRange(): InclusiveRange + } + } + `, + options, + ) + + errs := RequireCheckerErrors(t, err, 1) + + assert.IsType(t, &sema.MissingTypeArgumentError{}, errs[0]) + }) + t.Run("Resource with InclusiveRange", func(t *testing.T) { t.Parallel() @@ -667,6 +705,44 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { assert.IsType(t, &sema.MissingTypeArgumentError{}, errs[0]) }) + t.Run("Contract with ResourceInterface with InclusiveRange", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheckWithOptions(t, + ` + contract C { + resource interface Foo { + fun getRange(): InclusiveRange + } + } + `, + options, + ) + + require.NoError(t, err) + }) + + t.Run("Contract with ResourceInterface with InclusiveRange", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheckWithOptions(t, + ` + contract C { + resource interface Foo { + fun getRange(): InclusiveRange + } + } + `, + options, + ) + + errs := RequireCheckerErrors(t, err, 1) + + assert.IsType(t, &sema.MissingTypeArgumentError{}, errs[0]) + }) + t.Run("Type with InclusiveRange", func(t *testing.T) { t.Parallel() From 90ede500b071ac1a7471f3450f2b1336c3ba239b Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Sun, 5 Nov 2023 19:58:53 +0530 Subject: [PATCH 112/121] Fix typo for lint --- runtime/tests/checker/typeargument_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/tests/checker/typeargument_test.go b/runtime/tests/checker/typeargument_test.go index 5c6d242edf..a2204e167a 100644 --- a/runtime/tests/checker/typeargument_test.go +++ b/runtime/tests/checker/typeargument_test.go @@ -486,7 +486,7 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { errs := RequireCheckerErrors(t, err, 2) - // 2 errors for the two occurences of InclusiveRange. + // 2 errors for the two occurrences of InclusiveRange. assert.IsType(t, &sema.MissingTypeArgumentError{}, errs[0]) assert.IsType(t, &sema.MissingTypeArgumentError{}, errs[1]) }) From 141012f82c5295e22964507ce477b83d93592424 Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Thu, 9 Nov 2023 22:49:08 +0530 Subject: [PATCH 113/121] Move instantiation checks to Type interface --- runtime/sema/checker.go | 91 +----------------------- runtime/sema/simple_type.go | 3 + runtime/sema/type.go | 137 ++++++++++++++++++++++++++++++++++++ 3 files changed, 141 insertions(+), 90 deletions(-) diff --git a/runtime/sema/checker.go b/runtime/sema/checker.go index 8d3d2e1ac9..59dcc4dedb 100644 --- a/runtime/sema/checker.go +++ b/runtime/sema/checker.go @@ -2048,7 +2048,7 @@ func (checker *Checker) checkTypeAnnotation(typeAnnotation TypeAnnotation, pos a } checker.checkInvalidInterfaceAsType(typeAnnotation.Type, pos) - checker.checkParameterizedTypeIsInstantiated(typeAnnotation.Type, pos) + typeAnnotation.Type.CheckInstantiated(pos, checker.memoryGauge, checker.report) } func (checker *Checker) checkInvalidInterfaceAsType(ty Type, pos ast.HasPosition) { @@ -2068,95 +2068,6 @@ func (checker *Checker) checkInvalidInterfaceAsType(ty Type, pos ast.HasPosition } } -func (checker *Checker) checkParameterizedTypeIsInstantiated(ty Type, pos ast.HasPosition) { - switch t := ty.(type) { - case *OptionalType: - checker.checkParameterizedTypeIsInstantiated(t.Type, pos) - - case ArrayType: - checker.checkParameterizedTypeIsInstantiated(t.ElementType(false), pos) - - case *DictionaryType: - checker.checkParameterizedTypeIsInstantiated(t.KeyType, pos) - checker.checkParameterizedTypeIsInstantiated(t.ValueType, pos) - - case *ReferenceType: - checker.checkParameterizedTypeIsInstantiated(t.Type, pos) - - case *FunctionType: - for _, tyParam := range t.TypeParameters { - checker.checkParameterizedTypeIsInstantiated(tyParam.TypeBound, pos) - } - - for _, param := range t.Parameters { - checker.checkParameterizedTypeIsInstantiated(param.TypeAnnotation.Type, pos) - } - - checker.checkParameterizedTypeIsInstantiated(t.ReturnTypeAnnotation.Type, pos) - - case *RestrictedType: - checker.checkParameterizedTypeIsInstantiated(t.Type, pos) - - case *CompositeType: - if t.EnumRawType != nil { - checker.checkParameterizedTypeIsInstantiated(t.EnumRawType, pos) - } - if t.baseType != nil { - checker.checkParameterizedTypeIsInstantiated(t.baseType, pos) - } - - for _, typ := range t.ImplicitTypeRequirementConformances { - checker.checkParameterizedTypeIsInstantiated(typ, pos) - } - - for _, typ := range t.ExplicitInterfaceConformances { - checker.checkParameterizedTypeIsInstantiated(typ, pos) - } - - case *InterfaceType: - for _, param := range t.InitializerParameters { - checker.checkParameterizedTypeIsInstantiated(param.TypeAnnotation.Type, pos) - } - - case ParameterizedType: - typeArgs := t.TypeArguments() - typeParameters := t.TypeParameters() - - typeArgumentCount := len(typeArgs) - typeParameterCount := len(typeParameters) - - if typeArgumentCount != typeParameterCount { - checker.report( - &InvalidTypeArgumentCountError{ - TypeParameterCount: typeParameterCount, - TypeArgumentCount: typeArgumentCount, - Range: ast.NewRange( - checker.memoryGauge, - pos.StartPosition(), - pos.EndPosition(checker.memoryGauge), - ), - }, - ) - } - - // Ensure that each non-optional typeparameter is non-nil. - for index, typeParam := range typeParameters { - if !typeParam.Optional && typeArgs[index] == nil { - checker.report( - &MissingTypeArgumentError{ - TypeArgumentName: typeParam.Name, - Range: ast.NewRange( - checker.memoryGauge, - pos.StartPosition(), - pos.EndPosition(checker.memoryGauge), - ), - }, - ) - } - } - } -} - func (checker *Checker) ValueActivationDepth() int { return checker.valueActivations.Depth() } diff --git a/runtime/sema/simple_type.go b/runtime/sema/simple_type.go index 1e8b30b0df..27d0c5442f 100644 --- a/runtime/sema/simple_type.go +++ b/runtime/sema/simple_type.go @@ -168,3 +168,6 @@ func (t *SimpleType) CompositeKind() common.CompositeKind { return common.CompositeKindStructure } } + +func (t *SimpleType) CheckInstantiated(pos ast.HasPosition, memoryGauge common.MemoryGauge, report func(err error)) { +} diff --git a/runtime/sema/type.go b/runtime/sema/type.go index 20db7a64cc..ee88cd8b31 100644 --- a/runtime/sema/type.go +++ b/runtime/sema/type.go @@ -174,6 +174,8 @@ type Type interface { Resolve(typeArguments *TypeParameterTypeOrderedMap) Type GetMembers() map[string]MemberResolver + + CheckInstantiated(pos ast.HasPosition, memoryGauge common.MemoryGauge, report func(err error)) } // ValueIndexableType is a type which can be indexed into using a value @@ -293,6 +295,49 @@ func MustInstantiate(t ParameterizedType, typeArguments ...Type) Type { ) } +func CheckParameterizedTypeInstantiated( + t ParameterizedType, + pos ast.HasPosition, + memoryGauge common.MemoryGauge, + report func(err error), +) { + typeArgs := t.TypeArguments() + typeParameters := t.TypeParameters() + + typeArgumentCount := len(typeArgs) + typeParameterCount := len(typeParameters) + + if typeArgumentCount != typeParameterCount { + report( + &InvalidTypeArgumentCountError{ + TypeParameterCount: typeParameterCount, + TypeArgumentCount: typeArgumentCount, + Range: ast.NewRange( + memoryGauge, + pos.StartPosition(), + pos.EndPosition(memoryGauge), + ), + }, + ) + } + + // Ensure that each non-optional typeparameter is non-nil. + for index, typeParam := range typeParameters { + if !typeParam.Optional && typeArgs[index] == nil { + report( + &MissingTypeArgumentError{ + TypeArgumentName: typeParam.Name, + Range: ast.NewRange( + memoryGauge, + pos.StartPosition(), + pos.EndPosition(memoryGauge), + ), + }, + ) + } + } +} + // TypeAnnotation type TypeAnnotation struct { @@ -694,6 +739,10 @@ func (t *OptionalType) Resolve(typeArguments *TypeParameterTypeOrderedMap) Type } } +func (t *OptionalType) CheckInstantiated(pos ast.HasPosition, memoryGauge common.MemoryGauge, report func(err error)) { + t.Type.CheckInstantiated(pos, memoryGauge, report) +} + const optionalTypeMapFunctionDocString = ` Returns an optional of the result of calling the given function with the value of this optional when it is not nil. @@ -903,6 +952,10 @@ func (t *GenericType) GetMembers() map[string]MemberResolver { return withBuiltinMembers(t, nil) } +func (t *GenericType) CheckInstantiated(pos ast.HasPosition, memoryGauge common.MemoryGauge, report func(err error)) { + t.TypeParameter.TypeBound.CheckInstantiated(pos, memoryGauge, report) +} + // IntegerRangedType type IntegerRangedType interface { @@ -1176,6 +1229,9 @@ func (t *NumericType) IsSuperType() bool { return t.isSuperType } +func (*NumericType) CheckInstantiated(pos ast.HasPosition, memoryGauge common.MemoryGauge, report func(err error)) { +} + // FixedPointNumericType represents all the types in the fixed-point range. type FixedPointNumericType struct { maxFractional *big.Int @@ -1369,6 +1425,9 @@ func (t *FixedPointNumericType) IsSuperType() bool { return t.isSuperType } +func (*FixedPointNumericType) CheckInstantiated(pos ast.HasPosition, memoryGauge common.MemoryGauge, report func(err error)) { +} + // Numeric types var ( @@ -2551,6 +2610,10 @@ func (t *VariableSizedType) Resolve(typeArguments *TypeParameterTypeOrderedMap) } } +func (t *VariableSizedType) CheckInstantiated(pos ast.HasPosition, memoryGauge common.MemoryGauge, report func(err error)) { + t.ElementType(false).CheckInstantiated(pos, memoryGauge, report) +} + // ConstantSizedType is a constant sized array type type ConstantSizedType struct { Type Type @@ -2707,6 +2770,10 @@ func (t *ConstantSizedType) Resolve(typeArguments *TypeParameterTypeOrderedMap) } } +func (t *ConstantSizedType) CheckInstantiated(pos ast.HasPosition, memoryGauge common.MemoryGauge, report func(err error)) { + t.ElementType(false).CheckInstantiated(pos, memoryGauge, report) +} + // Parameter func formatParameter(spaces bool, label, identifier, typeAnnotation string) string { @@ -3372,6 +3439,18 @@ func (t *FunctionType) initializeMemberResolvers() { }) } +func (t *FunctionType) CheckInstantiated(pos ast.HasPosition, memoryGauge common.MemoryGauge, report func(err error)) { + for _, tyParam := range t.TypeParameters { + tyParam.TypeBound.CheckInstantiated(pos, memoryGauge, report) + } + + for _, param := range t.Parameters { + param.TypeAnnotation.Type.CheckInstantiated(pos, memoryGauge, report) + } + + t.ReturnTypeAnnotation.Type.CheckInstantiated(pos, memoryGauge, report) +} + type ArgumentExpressionsCheck func( checker *Checker, argumentExpressions []ast.Expression, @@ -4362,6 +4441,24 @@ func (t *CompositeType) SetNestedType(name string, nestedType ContainedType) { nestedType.SetContainerType(t) } +func (t *CompositeType) CheckInstantiated(pos ast.HasPosition, memoryGauge common.MemoryGauge, report func(err error)) { + if t.EnumRawType != nil { + t.EnumRawType.CheckInstantiated(pos, memoryGauge, report) + } + + if t.baseType != nil { + t.baseType.CheckInstantiated(pos, memoryGauge, report) + } + + for _, typ := range t.ImplicitTypeRequirementConformances { + typ.CheckInstantiated(pos, memoryGauge, report) + } + + for _, typ := range t.ExplicitInterfaceConformances { + typ.CheckInstantiated(pos, memoryGauge, report) + } +} + // Member type Member struct { @@ -4848,6 +4945,12 @@ func (t *InterfaceType) FieldPosition(name string, declaration *ast.InterfaceDec return declaration.Members.FieldPosition(name, declaration.CompositeKind) } +func (t *InterfaceType) CheckInstantiated(pos ast.HasPosition, memoryGauge common.MemoryGauge, report func(err error)) { + for _, param := range t.InitializerParameters { + param.TypeAnnotation.Type.CheckInstantiated(pos, memoryGauge, report) + } +} + // DictionaryType consists of the key and value type // for all key-value pairs in the dictionary: // All keys have to be a subtype of the key type, @@ -4977,6 +5080,11 @@ func (t *DictionaryType) RewriteWithRestrictedTypes() (Type, bool) { } } +func (t *DictionaryType) CheckInstantiated(pos ast.HasPosition, memoryGauge common.MemoryGauge, report func(err error)) { + t.KeyType.CheckInstantiated(pos, memoryGauge, report) + t.ValueType.CheckInstantiated(pos, memoryGauge, report) +} + const dictionaryTypeContainsKeyFunctionDocString = ` Returns true if the given key is in the dictionary ` @@ -5448,6 +5556,10 @@ func (t *InclusiveRangeType) TypeArguments() []Type { } } +func (t *InclusiveRangeType) CheckInstantiated(pos ast.HasPosition, memoryGauge common.MemoryGauge, report func(err error)) { + CheckParameterizedTypeInstantiated(t, pos, memoryGauge, report) +} + var inclusiveRangeTypeParameter = &TypeParameter{ Name: "T", TypeBound: IntegerType, @@ -5806,6 +5918,10 @@ func (t *ReferenceType) Resolve(typeArguments *TypeParameterTypeOrderedMap) Type } } +func (t *ReferenceType) CheckInstantiated(pos ast.HasPosition, memoryGauge common.MemoryGauge, report func(err error)) { + t.Type.CheckInstantiated(pos, memoryGauge, report) +} + const AddressTypeName = "Address" // AddressType represents the address type @@ -5901,6 +6017,9 @@ func (t *AddressType) Resolve(_ *TypeParameterTypeOrderedMap) Type { return t } +func (*AddressType) CheckInstantiated(pos ast.HasPosition, memoryGauge common.MemoryGauge, report func(err error)) { +} + const AddressTypeToBytesFunctionName = `toBytes` var AddressTypeToBytesFunctionType = &FunctionType{ @@ -6816,6 +6935,16 @@ func (t *TransactionType) Resolve(_ *TypeParameterTypeOrderedMap) Type { return t } +func (t *TransactionType) CheckInstantiated(pos ast.HasPosition, memoryGauge common.MemoryGauge, report func(err error)) { + for _, param := range t.PrepareParameters { + param.TypeAnnotation.Type.CheckInstantiated(pos, memoryGauge, report) + } + + for _, param := range t.Parameters { + param.TypeAnnotation.Type.CheckInstantiated(pos, memoryGauge, report) + } +} + // RestrictedType // // No restrictions implies the type is fully restricted, @@ -7121,6 +7250,10 @@ func (t *RestrictedType) IsValidIndexingType(ty Type) bool { attachmentType.IsResourceType() == t.IsResourceType() } +func (t *RestrictedType) CheckInstantiated(pos ast.HasPosition, memoryGauge common.MemoryGauge, report func(err error)) { + t.Type.CheckInstantiated(pos, memoryGauge, report) +} + // CapabilityType type CapabilityType struct { @@ -7324,6 +7457,10 @@ func (t *CapabilityType) TypeArguments() []Type { } } +func (t *CapabilityType) CheckInstantiated(pos ast.HasPosition, memoryGauge common.MemoryGauge, report func(err error)) { + CheckParameterizedTypeInstantiated(t, pos, memoryGauge, report) +} + func CapabilityTypeBorrowFunctionType(borrowType Type) *FunctionType { var typeParameters []*TypeParameter From 460533d060f7c7e7b49848036c12d398f8e3209f Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Fri, 10 Nov 2023 21:13:24 +0530 Subject: [PATCH 114/121] Declare and use a test function --- runtime/tests/checker/typeargument_test.go | 122 ++++++++------------- 1 file changed, 46 insertions(+), 76 deletions(-) diff --git a/runtime/tests/checker/typeargument_test.go b/runtime/tests/checker/typeargument_test.go index a2204e167a..64981bba15 100644 --- a/runtime/tests/checker/typeargument_test.go +++ b/runtime/tests/checker/typeargument_test.go @@ -159,21 +159,25 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { t.Parallel() - baseValueActivation := sema.NewVariableActivation(sema.BaseValueActivation) - baseValueActivation.DeclareValue(stdlib.InclusiveRangeConstructorFunction) - options := ParseAndCheckOptions{ - Config: &sema.Config{ - BaseValueActivation: baseValueActivation, - }, + test := func(t *testing.T, code string) error { + baseValueActivation := sema.NewVariableActivation(sema.BaseValueActivation) + baseValueActivation.DeclareValue(stdlib.InclusiveRangeConstructorFunction) + options := ParseAndCheckOptions{ + Config: &sema.Config{ + BaseValueActivation: baseValueActivation, + }, + } + + _, err := ParseAndCheckWithOptions(t, code, options) + return err } t.Run("InclusiveRange", func(t *testing.T) { t.Parallel() - _, err := ParseAndCheckWithOptions(t, + err := test(t, "let inclusiveRange: InclusiveRange = InclusiveRange(1, 10)", - options, ) require.NoError(t, err) @@ -183,9 +187,8 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { t.Parallel() - _, err := ParseAndCheckWithOptions(t, + err := test(t, "let inclusiveRange: InclusiveRange = InclusiveRange(1, 10)", - options, ) errs := RequireCheckerErrors(t, err, 1) @@ -197,9 +200,8 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { t.Parallel() - _, err := ParseAndCheckWithOptions(t, + err := test(t, "let r: [InclusiveRange] = []", - options, ) require.NoError(t, err) @@ -209,9 +211,8 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { t.Parallel() - _, err := ParseAndCheckWithOptions(t, + err := test(t, "let r: [InclusiveRange] = []", - options, ) errs := RequireCheckerErrors(t, err, 1) @@ -223,9 +224,8 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { t.Parallel() - _, err := ParseAndCheckWithOptions(t, + err := test(t, "let r: [InclusiveRange; 2] = [InclusiveRange(1, 2), InclusiveRange(3, 4)]", - options, ) require.NoError(t, err) @@ -235,9 +235,8 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { t.Parallel() - _, err := ParseAndCheckWithOptions(t, + err := test(t, "let r: [InclusiveRange; 2] = [InclusiveRange(1, 2), InclusiveRange(3, 4)]", - options, ) errs := RequireCheckerErrors(t, err, 1) @@ -249,9 +248,8 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { t.Parallel() - _, err := ParseAndCheckWithOptions(t, + err := test(t, "let r: InclusiveRange? = nil", - options, ) require.NoError(t, err) @@ -261,9 +259,8 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { t.Parallel() - _, err := ParseAndCheckWithOptions(t, + err := test(t, "let r: InclusiveRange? = nil", - options, ) errs := RequireCheckerErrors(t, err, 1) @@ -275,9 +272,8 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { t.Parallel() - _, err := ParseAndCheckWithOptions(t, + err := test(t, "let r: {Int: InclusiveRange} = {}", - options, ) require.NoError(t, err) @@ -287,9 +283,8 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { t.Parallel() - _, err := ParseAndCheckWithOptions(t, + err := test(t, "let r: {Int: InclusiveRange} = {}", - options, ) errs := RequireCheckerErrors(t, err, 1) @@ -301,7 +296,7 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { t.Parallel() - _, err := ParseAndCheckWithOptions(t, + err := test(t, ` struct Foo { let a: InclusiveRange @@ -311,7 +306,6 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { } } `, - options, ) require.NoError(t, err) @@ -321,7 +315,7 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { t.Parallel() - _, err := ParseAndCheckWithOptions(t, + err := test(t, ` struct Foo { let a: InclusiveRange @@ -331,7 +325,6 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { } } `, - options, ) errs := RequireCheckerErrors(t, err, 1) @@ -343,7 +336,7 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { t.Parallel() - _, err := ParseAndCheckWithOptions(t, + err := test(t, ` struct Bar { let a: InclusiveRange @@ -361,7 +354,6 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { } } `, - options, ) require.NoError(t, err) @@ -371,7 +363,7 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { t.Parallel() - _, err := ParseAndCheckWithOptions(t, + err := test(t, ` struct Bar { let a: InclusiveRange @@ -389,7 +381,6 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { } } `, - options, ) errs := RequireCheckerErrors(t, err, 1) @@ -401,7 +392,7 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { t.Parallel() - _, err := ParseAndCheckWithOptions(t, + err := test(t, ` contract C { struct Foo { @@ -413,7 +404,6 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { } } `, - options, ) require.NoError(t, err) @@ -423,7 +413,7 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { t.Parallel() - _, err := ParseAndCheckWithOptions(t, + err := test(t, ` contract C { struct Foo { @@ -435,7 +425,6 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { } } `, - options, ) errs := RequireCheckerErrors(t, err, 1) @@ -447,7 +436,7 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { t.Parallel() - _, err := ParseAndCheckWithOptions(t, + err := test(t, ` struct Bar { let f: ((): InclusiveRange) @@ -459,7 +448,6 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { } } `, - options, ) require.NoError(t, err) @@ -469,7 +457,7 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { t.Parallel() - _, err := ParseAndCheckWithOptions(t, + err := test(t, ` struct Bar { let f: ((): InclusiveRange) @@ -481,7 +469,6 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { } } `, - options, ) errs := RequireCheckerErrors(t, err, 2) @@ -495,13 +482,12 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { t.Parallel() - _, err := ParseAndCheckWithOptions(t, + err := test(t, ` struct interface Bar { fun getRange(): InclusiveRange } `, - options, ) require.NoError(t, err) @@ -511,13 +497,12 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { t.Parallel() - _, err := ParseAndCheckWithOptions(t, + err := test(t, ` struct interface Bar { fun getRange(): InclusiveRange } `, - options, ) errs := RequireCheckerErrors(t, err, 1) @@ -529,7 +514,7 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { t.Parallel() - _, err := ParseAndCheckWithOptions(t, + err := test(t, ` resource Foo { let a: InclusiveRange @@ -539,7 +524,6 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { } } `, - options, ) require.NoError(t, err) @@ -549,7 +533,7 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { t.Parallel() - _, err := ParseAndCheckWithOptions(t, + err := test(t, ` contract C { struct interface Foo { @@ -557,7 +541,6 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { } } `, - options, ) require.NoError(t, err) @@ -567,7 +550,7 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { t.Parallel() - _, err := ParseAndCheckWithOptions(t, + err := test(t, ` contract C { struct interface Foo { @@ -575,7 +558,6 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { } } `, - options, ) errs := RequireCheckerErrors(t, err, 1) @@ -587,7 +569,7 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { t.Parallel() - _, err := ParseAndCheckWithOptions(t, + err := test(t, ` resource Foo { let a: InclusiveRange @@ -597,7 +579,6 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { } } `, - options, ) errs := RequireCheckerErrors(t, err, 1) @@ -609,7 +590,7 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { t.Parallel() - _, err := ParseAndCheckWithOptions(t, + err := test(t, ` resource Bar { let a: InclusiveRange @@ -631,7 +612,6 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { } } `, - options, ) require.NoError(t, err) @@ -641,7 +621,7 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { t.Parallel() - _, err := ParseAndCheckWithOptions(t, + err := test(t, ` resource Bar { let a: InclusiveRange @@ -663,7 +643,6 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { } } `, - options, ) errs := RequireCheckerErrors(t, err, 1) @@ -675,13 +654,12 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { t.Parallel() - _, err := ParseAndCheckWithOptions(t, + err := test(t, ` resource interface Bar { fun getRange(): InclusiveRange } `, - options, ) require.NoError(t, err) @@ -691,13 +669,12 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { t.Parallel() - _, err := ParseAndCheckWithOptions(t, + err := test(t, ` resource interface Bar { fun getRange(): InclusiveRange } `, - options, ) errs := RequireCheckerErrors(t, err, 1) @@ -709,7 +686,7 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { t.Parallel() - _, err := ParseAndCheckWithOptions(t, + err := test(t, ` contract C { resource interface Foo { @@ -717,7 +694,6 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { } } `, - options, ) require.NoError(t, err) @@ -727,7 +703,7 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { t.Parallel() - _, err := ParseAndCheckWithOptions(t, + err := test(t, ` contract C { resource interface Foo { @@ -735,7 +711,6 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { } } `, - options, ) errs := RequireCheckerErrors(t, err, 1) @@ -747,13 +722,12 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { t.Parallel() - _, err := ParseAndCheckWithOptions(t, + err := test(t, ` pub fun main(): Type { return Type>() } `, - options, ) require.NoError(t, err) @@ -763,13 +737,12 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { t.Parallel() - _, err := ParseAndCheckWithOptions(t, + err := test(t, ` pub fun main(): Type { return Type() } `, - options, ) errs := RequireCheckerErrors(t, err, 1) @@ -781,9 +754,8 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { t.Parallel() - _, err := ParseAndCheckWithOptions(t, + err := test(t, "event Foo(bar: InclusiveRange)", - options, ) errs := RequireCheckerErrors(t, err, 1) @@ -795,9 +767,8 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { t.Parallel() - _, err := ParseAndCheckWithOptions(t, + err := test(t, "event Foo(bar: InclusiveRange)", - options, ) errs := RequireCheckerErrors(t, err, 2) @@ -810,7 +781,7 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { t.Parallel() - _, err := ParseAndCheckWithOptions(t, + err := test(t, ` pub fun main(): Direction { return Direction.RIGHT @@ -823,7 +794,6 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { pub case RIGHT } `, - options, ) require.NoError(t, err) From 21c83f87cec47d57ac0f2662e436e9045ddbf442 Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Fri, 10 Nov 2023 21:16:58 +0530 Subject: [PATCH 115/121] Add NO-OP comments and replace unused parameters with _ --- runtime/sema/simple_type.go | 3 ++- runtime/sema/type.go | 9 ++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/runtime/sema/simple_type.go b/runtime/sema/simple_type.go index 27d0c5442f..c9ea9941bf 100644 --- a/runtime/sema/simple_type.go +++ b/runtime/sema/simple_type.go @@ -169,5 +169,6 @@ func (t *SimpleType) CompositeKind() common.CompositeKind { } } -func (t *SimpleType) CheckInstantiated(pos ast.HasPosition, memoryGauge common.MemoryGauge, report func(err error)) { +func (t *SimpleType) CheckInstantiated(_ ast.HasPosition, _ common.MemoryGauge, _ func(err error)) { + // NO-OP } diff --git a/runtime/sema/type.go b/runtime/sema/type.go index ee88cd8b31..dae4e2d9c2 100644 --- a/runtime/sema/type.go +++ b/runtime/sema/type.go @@ -1229,7 +1229,8 @@ func (t *NumericType) IsSuperType() bool { return t.isSuperType } -func (*NumericType) CheckInstantiated(pos ast.HasPosition, memoryGauge common.MemoryGauge, report func(err error)) { +func (*NumericType) CheckInstantiated(_ ast.HasPosition, _ common.MemoryGauge, _ func(err error)) { + // NO-OP } // FixedPointNumericType represents all the types in the fixed-point range. @@ -1425,7 +1426,8 @@ func (t *FixedPointNumericType) IsSuperType() bool { return t.isSuperType } -func (*FixedPointNumericType) CheckInstantiated(pos ast.HasPosition, memoryGauge common.MemoryGauge, report func(err error)) { +func (*FixedPointNumericType) CheckInstantiated(_ ast.HasPosition, _ common.MemoryGauge, _ func(err error)) { + // NO-OP } // Numeric types @@ -6017,7 +6019,8 @@ func (t *AddressType) Resolve(_ *TypeParameterTypeOrderedMap) Type { return t } -func (*AddressType) CheckInstantiated(pos ast.HasPosition, memoryGauge common.MemoryGauge, report func(err error)) { +func (*AddressType) CheckInstantiated(_ ast.HasPosition, _ common.MemoryGauge, _ func(err error)) { + // NO-OP } const AddressTypeToBytesFunctionName = `toBytes` From f2adc9173c94f02c12d81f26fc79f2b5c718ca5a Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Sat, 2 Dec 2023 15:34:11 +0530 Subject: [PATCH 116/121] Remove redundant check --- runtime/sema/type.go | 17 +---------------- runtime/tests/checker/typeargument_test.go | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/runtime/sema/type.go b/runtime/sema/type.go index dae4e2d9c2..6343f6e6aa 100644 --- a/runtime/sema/type.go +++ b/runtime/sema/type.go @@ -304,22 +304,7 @@ func CheckParameterizedTypeInstantiated( typeArgs := t.TypeArguments() typeParameters := t.TypeParameters() - typeArgumentCount := len(typeArgs) - typeParameterCount := len(typeParameters) - - if typeArgumentCount != typeParameterCount { - report( - &InvalidTypeArgumentCountError{ - TypeParameterCount: typeParameterCount, - TypeArgumentCount: typeArgumentCount, - Range: ast.NewRange( - memoryGauge, - pos.StartPosition(), - pos.EndPosition(memoryGauge), - ), - }, - ) - } + // The check for the argument and parameter count already happens in the checker, so we skip that here. // Ensure that each non-optional typeparameter is non-nil. for index, typeParam := range typeParameters { diff --git a/runtime/tests/checker/typeargument_test.go b/runtime/tests/checker/typeargument_test.go index 64981bba15..ba193cb82a 100644 --- a/runtime/tests/checker/typeargument_test.go +++ b/runtime/tests/checker/typeargument_test.go @@ -183,6 +183,20 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { require.NoError(t, err) }) + t.Run("InclusiveRange", func(t *testing.T) { + + t.Parallel() + + err := test(t, + "let inclusiveRange: InclusiveRange = InclusiveRange(1, 10)", + ) + + errs := RequireCheckerErrors(t, err, 2) + + assert.IsType(t, &sema.InvalidTypeArgumentCountError{}, errs[0]) + assert.IsType(t, &sema.MissingTypeArgumentError{}, errs[1]) + }) + t.Run("InclusiveRange", func(t *testing.T) { t.Parallel() From a5beda930355096a3487c43e73b0545250fcc685 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Thu, 7 Dec 2023 10:59:16 -0800 Subject: [PATCH 117/121] add tests for functrion types --- runtime/tests/checker/typeargument_test.go | 140 ++++++++++++++++++++- 1 file changed, 138 insertions(+), 2 deletions(-) diff --git a/runtime/tests/checker/typeargument_test.go b/runtime/tests/checker/typeargument_test.go index ba193cb82a..0a23b4da87 100644 --- a/runtime/tests/checker/typeargument_test.go +++ b/runtime/tests/checker/typeargument_test.go @@ -24,6 +24,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/onflow/cadence/runtime/common" "github.com/onflow/cadence/runtime/sema" "github.com/onflow/cadence/runtime/stdlib" ) @@ -812,6 +813,141 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { require.NoError(t, err) }) + + testFunctionTpe := func(t *testing.T, functionType *sema.FunctionType) error { + baseTypeActivation := sema.NewVariableActivation(sema.BaseTypeActivation) + baseTypeActivation.DeclareType(stdlib.StandardLibraryType{ + Name: "TestFunc", + Kind: common.DeclarationKindType, + Type: functionType, + }) + + options := ParseAndCheckOptions{ + Config: &sema.Config{ + BaseTypeActivation: baseTypeActivation, + }, + } + + _, err := ParseAndCheckWithOptions(t, "fun test(testFunc: TestFunc) {}", options) + return err + } + + t.Run("Function type, return type not instantiated", func(t *testing.T) { + + t.Parallel() + err := testFunctionTpe(t, + &sema.FunctionType{ + ReturnTypeAnnotation: sema.NewTypeAnnotation( + &sema.InclusiveRangeType{}, + ), + }, + ) + + errs := RequireCheckerErrors(t, err, 1) + + assert.IsType(t, &sema.MissingTypeArgumentError{}, errs[0]) + }) + + t.Run("Function type, return type instantiated", func(t *testing.T) { + + t.Parallel() + err := testFunctionTpe(t, + &sema.FunctionType{ + ReturnTypeAnnotation: sema.NewTypeAnnotation( + &sema.InclusiveRangeType{ + MemberType: sema.IntType, + }, + ), + }, + ) + + require.NoError(t, err) + }) + + t.Run("Function type, parameter type not instantiated", func(t *testing.T) { + + t.Parallel() + err := testFunctionTpe(t, + &sema.FunctionType{ + Parameters: []sema.Parameter{ + { + Identifier: "a", + TypeAnnotation: sema.NewTypeAnnotation( + &sema.InclusiveRangeType{}, + ), + }, + }, + ReturnTypeAnnotation: sema.VoidTypeAnnotation, + }, + ) + + errs := RequireCheckerErrors(t, err, 1) + + assert.IsType(t, &sema.MissingTypeArgumentError{}, errs[0]) + }) + + t.Run("Function type, parameter type instantiated", func(t *testing.T) { + + t.Parallel() + err := testFunctionTpe(t, + &sema.FunctionType{ + Parameters: []sema.Parameter{ + { + Identifier: "a", + TypeAnnotation: sema.NewTypeAnnotation( + &sema.InclusiveRangeType{ + MemberType: sema.IntType, + }, + ), + }, + }, + ReturnTypeAnnotation: sema.VoidTypeAnnotation, + }, + ) + + require.NoError(t, err) + }) + + t.Run("Function type, type parameter type bound not instantiated", func(t *testing.T) { + + t.Parallel() + err := testFunctionTpe(t, + &sema.FunctionType{ + TypeParameters: []*sema.TypeParameter{ + { + Name: "T", + TypeBound: &sema.InclusiveRangeType{}, + }, + }, + ReturnTypeAnnotation: sema.VoidTypeAnnotation, + }, + ) + + errs := RequireCheckerErrors(t, err, 1) + + assert.IsType(t, &sema.MissingTypeArgumentError{}, errs[0]) + }) + + t.Run("Function type,type parameter type bound instantiated", func(t *testing.T) { + + t.Parallel() + err := testFunctionTpe(t, + &sema.FunctionType{ + TypeParameters: []*sema.TypeParameter{ + { + Name: "T", + TypeBound: &sema.InclusiveRangeType{ + MemberType: sema.IntType, + }, + }, + }, + ReturnTypeAnnotation: sema.VoidTypeAnnotation, + }, + ) + + require.NoError(t, err) + }) + } func TestCheckTypeArgumentSubtyping(t *testing.T) { @@ -904,7 +1040,7 @@ func TestCheckTypeArgumentSubtyping(t *testing.T) { `, &sema.CapabilityType{}, ) - require.NotNil(t, checker) + require.NoError(t, err) capType := RequireGlobalValue(t, checker.Elaboration, "cap") require.IsType(t, @@ -945,7 +1081,7 @@ func TestCheckTypeArgumentSubtyping(t *testing.T) { }, }, ) - require.NotNil(t, checker) + require.NoError(t, err) assert.Equal(t, &sema.CapabilityType{ From 3bd5990eaa54abb22331a5f887c1af9c4ac082e4 Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Fri, 8 Dec 2023 21:28:37 +0530 Subject: [PATCH 118/121] Use function instead of sharing object --- runtime/tests/checker/range_value_test.go | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/runtime/tests/checker/range_value_test.go b/runtime/tests/checker/range_value_test.go index 9907de2742..ae6ae2b379 100644 --- a/runtime/tests/checker/range_value_test.go +++ b/runtime/tests/checker/range_value_test.go @@ -404,13 +404,15 @@ func TestInclusiveRangeNonLeafIntegerTypes(t *testing.T) { t.Parallel() - baseValueActivation := sema.NewVariableActivation(sema.BaseValueActivation) - baseValueActivation.DeclareValue(stdlib.InclusiveRangeConstructorFunction) - - options := ParseAndCheckOptions{ - Config: &sema.Config{ - BaseValueActivation: baseValueActivation, - }, + newOptions := func() ParseAndCheckOptions { + baseValueActivation := sema.NewVariableActivation(sema.BaseValueActivation) + baseValueActivation.DeclareValue(stdlib.InclusiveRangeConstructorFunction) + + return ParseAndCheckOptions{ + Config: &sema.Config{ + BaseValueActivation: baseValueActivation, + }, + } } test := func(t *testing.T, ty sema.Type) { @@ -421,7 +423,7 @@ func TestInclusiveRangeNonLeafIntegerTypes(t *testing.T) { let a: %[1]s = 0 let b: %[1]s = 10 var range = InclusiveRange<%[1]s>(a, b) - `, ty), options) + `, ty), newOptions()) errs := RequireCheckerErrors(t, err, 1) assert.IsType(t, &sema.InvalidTypeArgumentError{}, errs[0]) @@ -434,7 +436,7 @@ func TestInclusiveRangeNonLeafIntegerTypes(t *testing.T) { let a: %[1]s = 0 let b: %[1]s = 10 var range: InclusiveRange<%[1]s> = InclusiveRange<%[1]s>(a, b) - `, ty), options) + `, ty), newOptions()) // One for the invocation and another for the type. errs := RequireCheckerErrors(t, err, 2) @@ -448,7 +450,7 @@ func TestInclusiveRangeNonLeafIntegerTypes(t *testing.T) { _, err := ParseAndCheckWithOptions(t, fmt.Sprintf(` let a: InclusiveRange = InclusiveRange(0, 10) let b: InclusiveRange<%s> = a - `, ty), options) + `, ty), newOptions()) errs := RequireCheckerErrors(t, err, 1) assert.IsType(t, &sema.InvalidTypeArgumentError{}, errs[0]) From f82c07fcd901217528fd10ffcccb916f4d2b2303 Mon Sep 17 00:00:00 2001 From: Supun Setunga Date: Fri, 8 Dec 2023 13:58:36 -0800 Subject: [PATCH 119/121] Remove unnecessary variable-type assertion --- runtime/tests/checker/typeargument_test.go | 45 +--------------------- 1 file changed, 2 insertions(+), 43 deletions(-) diff --git a/runtime/tests/checker/typeargument_test.go b/runtime/tests/checker/typeargument_test.go index 0a23b4da87..6fd00c22fb 100644 --- a/runtime/tests/checker/typeargument_test.go +++ b/runtime/tests/checker/typeargument_test.go @@ -1033,36 +1033,15 @@ func TestCheckTypeArgumentSubtyping(t *testing.T) { t.Parallel() - checker, err := parseAndCheckWithTestValue(t, + _, err := parseAndCheckWithTestValue(t, ` let cap: Capability = test let cap2: Capability<&Int> = cap `, &sema.CapabilityType{}, ) - require.NoError(t, err) - - capType := RequireGlobalValue(t, checker.Elaboration, "cap") - require.IsType(t, - &sema.CapabilityType{}, - capType, - ) - require.Nil(t, capType.(*sema.CapabilityType).BorrowType) - - cap2Type := RequireGlobalValue(t, checker.Elaboration, "cap2") - require.IsType(t, - &sema.CapabilityType{}, - cap2Type, - ) - require.Equal(t, - &sema.ReferenceType{ - Type: sema.IntType, - }, - cap2Type.(*sema.CapabilityType).BorrowType, - ) errs := RequireCheckerErrors(t, err, 1) - assert.IsType(t, &sema.TypeMismatchError{}, errs[0]) }) @@ -1070,7 +1049,7 @@ func TestCheckTypeArgumentSubtyping(t *testing.T) { t.Parallel() - checker, err := parseAndCheckWithTestValue(t, + _, err := parseAndCheckWithTestValue(t, ` let cap: Capability<&String> = test let cap2: Capability<&Int> = cap @@ -1081,28 +1060,8 @@ func TestCheckTypeArgumentSubtyping(t *testing.T) { }, }, ) - require.NoError(t, err) - - assert.Equal(t, - &sema.CapabilityType{ - BorrowType: &sema.ReferenceType{ - Type: sema.StringType, - }, - }, - RequireGlobalValue(t, checker.Elaboration, "cap"), - ) - - assert.Equal(t, - &sema.CapabilityType{ - BorrowType: &sema.ReferenceType{ - Type: sema.IntType, - }, - }, - RequireGlobalValue(t, checker.Elaboration, "cap2"), - ) errs := RequireCheckerErrors(t, err, 1) - assert.IsType(t, &sema.TypeMismatchError{}, errs[0]) }) } From 6cc271f2b1594e90d27b609ea57e60c73e5895f3 Mon Sep 17 00:00:00 2001 From: Supun Setunga Date: Fri, 5 Jan 2024 15:29:05 -0800 Subject: [PATCH 120/121] Update inclusive range type to match stable cadence changes --- encoding/ccf/ccf_test.go | 6 +- encoding/json/encode.go | 80 ++--- encoding/json/encoding_test.go | 4 +- runtime/common/memorykind_string.go | 326 +++++++++--------- runtime/common/metering.go | 4 +- runtime/convertValues.go | 13 - runtime/convertValues_test.go | 44 ++- .../imported_values_memory_metering_test.go | 4 +- runtime/interpreter/statictype.go | 9 +- runtime/interpreter/statictype_test.go | 2 +- runtime/interpreter/value.go | 33 +- runtime/interpreter/value_range.go | 10 +- runtime/program_params_validation_test.go | 10 +- runtime/runtime_test.go | 24 +- runtime/sema/type.go | 45 ++- runtime/tests/checker/for_test.go | 9 +- runtime/tests/checker/operations_test.go | 4 +- runtime/tests/checker/range_value_test.go | 26 +- runtime/tests/checker/typeargument_test.go | 44 ++- .../tests/interpreter/dynamic_casting_test.go | 8 +- runtime/tests/interpreter/for_test.go | 12 +- runtime/tests/interpreter/interface_test.go | 6 +- runtime/tests/interpreter/range_value_test.go | 25 +- values.go | 1 - values_test.go | 2 +- 25 files changed, 417 insertions(+), 334 deletions(-) diff --git a/encoding/ccf/ccf_test.go b/encoding/ccf/ccf_test.go index 94bc6080e5..b6647e9e15 100644 --- a/encoding/ccf/ccf_test.go +++ b/encoding/ccf/ccf_test.go @@ -5708,7 +5708,7 @@ func TestEncodeInclusiveRange(t *testing.T) { cadence.NewInt256(10), cadence.NewInt256(20), cadence.NewInt256(5), - ).WithType(cadence.NewInclusiveRangeType(cadence.NewInt256Type())) + ).WithType(cadence.NewInclusiveRangeType(cadence.Int256Type)) }(), expected: []byte{ // language=json, format=json-cdc @@ -5760,7 +5760,7 @@ func TestEncodeInclusiveRange(t *testing.T) { cadence.NewInt8(10), cadence.NewInt8(20), cadence.NewInt8(5), - ).WithType(cadence.NewInclusiveRangeType(cadence.NewInt8Type())) + ).WithType(cadence.NewInclusiveRangeType(cadence.Int8Type)) }(), expected: []byte{ // language=json, format=json-cdc @@ -8236,7 +8236,7 @@ func TestEncodeType(t *testing.T) { t, cadence.TypeValue{ StaticType: &cadence.InclusiveRangeType{ - ElementType: cadence.IntType{}, + ElementType: cadence.IntType, }, }, []byte{ diff --git a/encoding/json/encode.go b/encoding/json/encode.go index f6d9c8bc58..9cc042d636 100644 --- a/encoding/json/encode.go +++ b/encoding/json/encode.go @@ -228,46 +228,46 @@ type jsonInclusiveRangeType struct { } const ( - voidTypeStr = "Void" - optionalTypeStr = "Optional" - boolTypeStr = "Bool" - characterTypeStr = "Character" - stringTypeStr = "String" - addressTypeStr = "Address" - intTypeStr = "Int" - int8TypeStr = "Int8" - int16TypeStr = "Int16" - int32TypeStr = "Int32" - int64TypeStr = "Int64" - int128TypeStr = "Int128" - int256TypeStr = "Int256" - uintTypeStr = "UInt" - uint8TypeStr = "UInt8" - uint16TypeStr = "UInt16" - uint32TypeStr = "UInt32" - uint64TypeStr = "UInt64" - uint128TypeStr = "UInt128" - uint256TypeStr = "UInt256" - word8TypeStr = "Word8" - word16TypeStr = "Word16" - word32TypeStr = "Word32" - word64TypeStr = "Word64" - word128TypeStr = "Word128" - word256TypeStr = "Word256" - fix64TypeStr = "Fix64" - ufix64TypeStr = "UFix64" - arrayTypeStr = "Array" - dictionaryTypeStr = "Dictionary" - structTypeStr = "Struct" - resourceTypeStr = "Resource" - attachmentTypeStr = "Attachment" - eventTypeStr = "Event" - contractTypeStr = "Contract" - pathTypeStr = "Path" - typeTypeStr = "Type" - capabilityTypeStr = "Capability" - enumTypeStr = "Enum" - functionTypeStr = "Function" + voidTypeStr = "Void" + optionalTypeStr = "Optional" + boolTypeStr = "Bool" + characterTypeStr = "Character" + stringTypeStr = "String" + addressTypeStr = "Address" + intTypeStr = "Int" + int8TypeStr = "Int8" + int16TypeStr = "Int16" + int32TypeStr = "Int32" + int64TypeStr = "Int64" + int128TypeStr = "Int128" + int256TypeStr = "Int256" + uintTypeStr = "UInt" + uint8TypeStr = "UInt8" + uint16TypeStr = "UInt16" + uint32TypeStr = "UInt32" + uint64TypeStr = "UInt64" + uint128TypeStr = "UInt128" + uint256TypeStr = "UInt256" + word8TypeStr = "Word8" + word16TypeStr = "Word16" + word32TypeStr = "Word32" + word64TypeStr = "Word64" + word128TypeStr = "Word128" + word256TypeStr = "Word256" + fix64TypeStr = "Fix64" + ufix64TypeStr = "UFix64" + arrayTypeStr = "Array" + dictionaryTypeStr = "Dictionary" + structTypeStr = "Struct" + resourceTypeStr = "Resource" + attachmentTypeStr = "Attachment" + eventTypeStr = "Event" + contractTypeStr = "Contract" + pathTypeStr = "Path" + typeTypeStr = "Type" + capabilityTypeStr = "Capability" + enumTypeStr = "Enum" + functionTypeStr = "Function" inclusiveRangeTypeStr = "InclusiveRange" ) diff --git a/encoding/json/encoding_test.go b/encoding/json/encoding_test.go index b9040e531e..00b33f6822 100644 --- a/encoding/json/encoding_test.go +++ b/encoding/json/encoding_test.go @@ -1453,7 +1453,7 @@ func TestEncodeInclusiveRange(t *testing.T) { cadence.NewInt256(10), cadence.NewInt256(20), cadence.NewInt256(5), - ).WithType(cadence.NewInclusiveRangeType(cadence.NewInt256Type())), + ).WithType(cadence.NewInclusiveRangeType(cadence.Int256Type)), // language=json ` { @@ -1870,7 +1870,7 @@ func TestEncodeType(t *testing.T) { t, cadence.TypeValue{ StaticType: &cadence.InclusiveRangeType{ - ElementType: cadence.IntType{}, + ElementType: cadence.IntType, }, }, // language=json diff --git a/runtime/common/memorykind_string.go b/runtime/common/memorykind_string.go index 6790aa4a22..65ac875cbe 100644 --- a/runtime/common/memorykind_string.go +++ b/runtime/common/memorykind_string.go @@ -45,170 +45,174 @@ func _() { _ = x[MemoryKindVariableSizedStaticType-34] _ = x[MemoryKindConstantSizedStaticType-35] _ = x[MemoryKindDictionaryStaticType-36] - _ = x[MemoryKindOptionalStaticType-37] - _ = x[MemoryKindIntersectionStaticType-38] - _ = x[MemoryKindEntitlementSetStaticAccess-39] - _ = x[MemoryKindEntitlementMapStaticAccess-40] - _ = x[MemoryKindReferenceStaticType-41] - _ = x[MemoryKindCapabilityStaticType-42] - _ = x[MemoryKindFunctionStaticType-43] - _ = x[MemoryKindCadenceVoidValue-44] - _ = x[MemoryKindCadenceOptionalValue-45] - _ = x[MemoryKindCadenceBoolValue-46] - _ = x[MemoryKindCadenceStringValue-47] - _ = x[MemoryKindCadenceCharacterValue-48] - _ = x[MemoryKindCadenceAddressValue-49] - _ = x[MemoryKindCadenceIntValue-50] - _ = x[MemoryKindCadenceNumberValue-51] - _ = x[MemoryKindCadenceArrayValueBase-52] - _ = x[MemoryKindCadenceArrayValueLength-53] - _ = x[MemoryKindCadenceDictionaryValue-54] - _ = x[MemoryKindCadenceKeyValuePair-55] - _ = x[MemoryKindCadenceStructValueBase-56] - _ = x[MemoryKindCadenceStructValueSize-57] - _ = x[MemoryKindCadenceResourceValueBase-58] - _ = x[MemoryKindCadenceAttachmentValueBase-59] - _ = x[MemoryKindCadenceResourceValueSize-60] - _ = x[MemoryKindCadenceAttachmentValueSize-61] - _ = x[MemoryKindCadenceEventValueBase-62] - _ = x[MemoryKindCadenceEventValueSize-63] - _ = x[MemoryKindCadenceContractValueBase-64] - _ = x[MemoryKindCadenceContractValueSize-65] - _ = x[MemoryKindCadenceEnumValueBase-66] - _ = x[MemoryKindCadenceEnumValueSize-67] - _ = x[MemoryKindCadencePathValue-68] - _ = x[MemoryKindCadenceTypeValue-69] - _ = x[MemoryKindCadenceCapabilityValue-70] - _ = x[MemoryKindCadenceFunctionValue-71] - _ = x[MemoryKindCadenceOptionalType-72] - _ = x[MemoryKindCadenceVariableSizedArrayType-73] - _ = x[MemoryKindCadenceConstantSizedArrayType-74] - _ = x[MemoryKindCadenceDictionaryType-75] - _ = x[MemoryKindCadenceField-76] - _ = x[MemoryKindCadenceParameter-77] - _ = x[MemoryKindCadenceTypeParameter-78] - _ = x[MemoryKindCadenceStructType-79] - _ = x[MemoryKindCadenceResourceType-80] - _ = x[MemoryKindCadenceAttachmentType-81] - _ = x[MemoryKindCadenceEventType-82] - _ = x[MemoryKindCadenceContractType-83] - _ = x[MemoryKindCadenceStructInterfaceType-84] - _ = x[MemoryKindCadenceResourceInterfaceType-85] - _ = x[MemoryKindCadenceContractInterfaceType-86] - _ = x[MemoryKindCadenceFunctionType-87] - _ = x[MemoryKindCadenceEntitlementSetAccess-88] - _ = x[MemoryKindCadenceEntitlementMapAccess-89] - _ = x[MemoryKindCadenceReferenceType-90] - _ = x[MemoryKindCadenceIntersectionType-91] - _ = x[MemoryKindCadenceCapabilityType-92] - _ = x[MemoryKindCadenceEnumType-93] - _ = x[MemoryKindRawString-94] - _ = x[MemoryKindAddressLocation-95] - _ = x[MemoryKindBytes-96] - _ = x[MemoryKindVariable-97] - _ = x[MemoryKindCompositeTypeInfo-98] - _ = x[MemoryKindCompositeField-99] - _ = x[MemoryKindInvocation-100] - _ = x[MemoryKindStorageMap-101] - _ = x[MemoryKindStorageKey-102] - _ = x[MemoryKindTypeToken-103] - _ = x[MemoryKindErrorToken-104] - _ = x[MemoryKindSpaceToken-105] - _ = x[MemoryKindProgram-106] - _ = x[MemoryKindIdentifier-107] - _ = x[MemoryKindArgument-108] - _ = x[MemoryKindBlock-109] - _ = x[MemoryKindFunctionBlock-110] - _ = x[MemoryKindParameter-111] - _ = x[MemoryKindParameterList-112] - _ = x[MemoryKindTypeParameter-113] - _ = x[MemoryKindTypeParameterList-114] - _ = x[MemoryKindTransfer-115] - _ = x[MemoryKindMembers-116] - _ = x[MemoryKindTypeAnnotation-117] - _ = x[MemoryKindDictionaryEntry-118] - _ = x[MemoryKindFunctionDeclaration-119] - _ = x[MemoryKindCompositeDeclaration-120] - _ = x[MemoryKindAttachmentDeclaration-121] - _ = x[MemoryKindInterfaceDeclaration-122] - _ = x[MemoryKindEntitlementDeclaration-123] - _ = x[MemoryKindEntitlementMappingElement-124] - _ = x[MemoryKindEntitlementMappingDeclaration-125] - _ = x[MemoryKindEnumCaseDeclaration-126] - _ = x[MemoryKindFieldDeclaration-127] - _ = x[MemoryKindTransactionDeclaration-128] - _ = x[MemoryKindImportDeclaration-129] - _ = x[MemoryKindVariableDeclaration-130] - _ = x[MemoryKindSpecialFunctionDeclaration-131] - _ = x[MemoryKindPragmaDeclaration-132] - _ = x[MemoryKindAssignmentStatement-133] - _ = x[MemoryKindBreakStatement-134] - _ = x[MemoryKindContinueStatement-135] - _ = x[MemoryKindEmitStatement-136] - _ = x[MemoryKindExpressionStatement-137] - _ = x[MemoryKindForStatement-138] - _ = x[MemoryKindIfStatement-139] - _ = x[MemoryKindReturnStatement-140] - _ = x[MemoryKindSwapStatement-141] - _ = x[MemoryKindSwitchStatement-142] - _ = x[MemoryKindWhileStatement-143] - _ = x[MemoryKindRemoveStatement-144] - _ = x[MemoryKindBooleanExpression-145] - _ = x[MemoryKindVoidExpression-146] - _ = x[MemoryKindNilExpression-147] - _ = x[MemoryKindStringExpression-148] - _ = x[MemoryKindIntegerExpression-149] - _ = x[MemoryKindFixedPointExpression-150] - _ = x[MemoryKindArrayExpression-151] - _ = x[MemoryKindDictionaryExpression-152] - _ = x[MemoryKindIdentifierExpression-153] - _ = x[MemoryKindInvocationExpression-154] - _ = x[MemoryKindMemberExpression-155] - _ = x[MemoryKindIndexExpression-156] - _ = x[MemoryKindConditionalExpression-157] - _ = x[MemoryKindUnaryExpression-158] - _ = x[MemoryKindBinaryExpression-159] - _ = x[MemoryKindFunctionExpression-160] - _ = x[MemoryKindCastingExpression-161] - _ = x[MemoryKindCreateExpression-162] - _ = x[MemoryKindDestroyExpression-163] - _ = x[MemoryKindReferenceExpression-164] - _ = x[MemoryKindForceExpression-165] - _ = x[MemoryKindPathExpression-166] - _ = x[MemoryKindAttachExpression-167] - _ = x[MemoryKindConstantSizedType-168] - _ = x[MemoryKindDictionaryType-169] - _ = x[MemoryKindFunctionType-170] - _ = x[MemoryKindInstantiationType-171] - _ = x[MemoryKindNominalType-172] - _ = x[MemoryKindOptionalType-173] - _ = x[MemoryKindReferenceType-174] - _ = x[MemoryKindIntersectionType-175] - _ = x[MemoryKindVariableSizedType-176] - _ = x[MemoryKindPosition-177] - _ = x[MemoryKindRange-178] - _ = x[MemoryKindElaboration-179] - _ = x[MemoryKindActivation-180] - _ = x[MemoryKindActivationEntries-181] - _ = x[MemoryKindVariableSizedSemaType-182] - _ = x[MemoryKindConstantSizedSemaType-183] - _ = x[MemoryKindDictionarySemaType-184] - _ = x[MemoryKindOptionalSemaType-185] - _ = x[MemoryKindIntersectionSemaType-186] - _ = x[MemoryKindReferenceSemaType-187] - _ = x[MemoryKindEntitlementSemaType-188] - _ = x[MemoryKindEntitlementMapSemaType-189] - _ = x[MemoryKindEntitlementRelationSemaType-190] - _ = x[MemoryKindCapabilitySemaType-191] - _ = x[MemoryKindOrderedMap-192] - _ = x[MemoryKindOrderedMapEntryList-193] - _ = x[MemoryKindOrderedMapEntry-194] - _ = x[MemoryKindLast-195] + _ = x[MemoryKindInclusiveRangeStaticType-37] + _ = x[MemoryKindOptionalStaticType-38] + _ = x[MemoryKindIntersectionStaticType-39] + _ = x[MemoryKindEntitlementSetStaticAccess-40] + _ = x[MemoryKindEntitlementMapStaticAccess-41] + _ = x[MemoryKindReferenceStaticType-42] + _ = x[MemoryKindCapabilityStaticType-43] + _ = x[MemoryKindFunctionStaticType-44] + _ = x[MemoryKindCadenceVoidValue-45] + _ = x[MemoryKindCadenceOptionalValue-46] + _ = x[MemoryKindCadenceBoolValue-47] + _ = x[MemoryKindCadenceStringValue-48] + _ = x[MemoryKindCadenceCharacterValue-49] + _ = x[MemoryKindCadenceAddressValue-50] + _ = x[MemoryKindCadenceIntValue-51] + _ = x[MemoryKindCadenceNumberValue-52] + _ = x[MemoryKindCadenceArrayValueBase-53] + _ = x[MemoryKindCadenceArrayValueLength-54] + _ = x[MemoryKindCadenceDictionaryValue-55] + _ = x[MemoryKindCadenceInclusiveRangeValue-56] + _ = x[MemoryKindCadenceKeyValuePair-57] + _ = x[MemoryKindCadenceStructValueBase-58] + _ = x[MemoryKindCadenceStructValueSize-59] + _ = x[MemoryKindCadenceResourceValueBase-60] + _ = x[MemoryKindCadenceAttachmentValueBase-61] + _ = x[MemoryKindCadenceResourceValueSize-62] + _ = x[MemoryKindCadenceAttachmentValueSize-63] + _ = x[MemoryKindCadenceEventValueBase-64] + _ = x[MemoryKindCadenceEventValueSize-65] + _ = x[MemoryKindCadenceContractValueBase-66] + _ = x[MemoryKindCadenceContractValueSize-67] + _ = x[MemoryKindCadenceEnumValueBase-68] + _ = x[MemoryKindCadenceEnumValueSize-69] + _ = x[MemoryKindCadencePathValue-70] + _ = x[MemoryKindCadenceTypeValue-71] + _ = x[MemoryKindCadenceCapabilityValue-72] + _ = x[MemoryKindCadenceFunctionValue-73] + _ = x[MemoryKindCadenceOptionalType-74] + _ = x[MemoryKindCadenceVariableSizedArrayType-75] + _ = x[MemoryKindCadenceConstantSizedArrayType-76] + _ = x[MemoryKindCadenceDictionaryType-77] + _ = x[MemoryKindCadenceInclusiveRangeType-78] + _ = x[MemoryKindCadenceField-79] + _ = x[MemoryKindCadenceParameter-80] + _ = x[MemoryKindCadenceTypeParameter-81] + _ = x[MemoryKindCadenceStructType-82] + _ = x[MemoryKindCadenceResourceType-83] + _ = x[MemoryKindCadenceAttachmentType-84] + _ = x[MemoryKindCadenceEventType-85] + _ = x[MemoryKindCadenceContractType-86] + _ = x[MemoryKindCadenceStructInterfaceType-87] + _ = x[MemoryKindCadenceResourceInterfaceType-88] + _ = x[MemoryKindCadenceContractInterfaceType-89] + _ = x[MemoryKindCadenceFunctionType-90] + _ = x[MemoryKindCadenceEntitlementSetAccess-91] + _ = x[MemoryKindCadenceEntitlementMapAccess-92] + _ = x[MemoryKindCadenceReferenceType-93] + _ = x[MemoryKindCadenceIntersectionType-94] + _ = x[MemoryKindCadenceCapabilityType-95] + _ = x[MemoryKindCadenceEnumType-96] + _ = x[MemoryKindRawString-97] + _ = x[MemoryKindAddressLocation-98] + _ = x[MemoryKindBytes-99] + _ = x[MemoryKindVariable-100] + _ = x[MemoryKindCompositeTypeInfo-101] + _ = x[MemoryKindCompositeField-102] + _ = x[MemoryKindInvocation-103] + _ = x[MemoryKindStorageMap-104] + _ = x[MemoryKindStorageKey-105] + _ = x[MemoryKindTypeToken-106] + _ = x[MemoryKindErrorToken-107] + _ = x[MemoryKindSpaceToken-108] + _ = x[MemoryKindProgram-109] + _ = x[MemoryKindIdentifier-110] + _ = x[MemoryKindArgument-111] + _ = x[MemoryKindBlock-112] + _ = x[MemoryKindFunctionBlock-113] + _ = x[MemoryKindParameter-114] + _ = x[MemoryKindParameterList-115] + _ = x[MemoryKindTypeParameter-116] + _ = x[MemoryKindTypeParameterList-117] + _ = x[MemoryKindTransfer-118] + _ = x[MemoryKindMembers-119] + _ = x[MemoryKindTypeAnnotation-120] + _ = x[MemoryKindDictionaryEntry-121] + _ = x[MemoryKindFunctionDeclaration-122] + _ = x[MemoryKindCompositeDeclaration-123] + _ = x[MemoryKindAttachmentDeclaration-124] + _ = x[MemoryKindInterfaceDeclaration-125] + _ = x[MemoryKindEntitlementDeclaration-126] + _ = x[MemoryKindEntitlementMappingElement-127] + _ = x[MemoryKindEntitlementMappingDeclaration-128] + _ = x[MemoryKindEnumCaseDeclaration-129] + _ = x[MemoryKindFieldDeclaration-130] + _ = x[MemoryKindTransactionDeclaration-131] + _ = x[MemoryKindImportDeclaration-132] + _ = x[MemoryKindVariableDeclaration-133] + _ = x[MemoryKindSpecialFunctionDeclaration-134] + _ = x[MemoryKindPragmaDeclaration-135] + _ = x[MemoryKindAssignmentStatement-136] + _ = x[MemoryKindBreakStatement-137] + _ = x[MemoryKindContinueStatement-138] + _ = x[MemoryKindEmitStatement-139] + _ = x[MemoryKindExpressionStatement-140] + _ = x[MemoryKindForStatement-141] + _ = x[MemoryKindIfStatement-142] + _ = x[MemoryKindReturnStatement-143] + _ = x[MemoryKindSwapStatement-144] + _ = x[MemoryKindSwitchStatement-145] + _ = x[MemoryKindWhileStatement-146] + _ = x[MemoryKindRemoveStatement-147] + _ = x[MemoryKindBooleanExpression-148] + _ = x[MemoryKindVoidExpression-149] + _ = x[MemoryKindNilExpression-150] + _ = x[MemoryKindStringExpression-151] + _ = x[MemoryKindIntegerExpression-152] + _ = x[MemoryKindFixedPointExpression-153] + _ = x[MemoryKindArrayExpression-154] + _ = x[MemoryKindDictionaryExpression-155] + _ = x[MemoryKindIdentifierExpression-156] + _ = x[MemoryKindInvocationExpression-157] + _ = x[MemoryKindMemberExpression-158] + _ = x[MemoryKindIndexExpression-159] + _ = x[MemoryKindConditionalExpression-160] + _ = x[MemoryKindUnaryExpression-161] + _ = x[MemoryKindBinaryExpression-162] + _ = x[MemoryKindFunctionExpression-163] + _ = x[MemoryKindCastingExpression-164] + _ = x[MemoryKindCreateExpression-165] + _ = x[MemoryKindDestroyExpression-166] + _ = x[MemoryKindReferenceExpression-167] + _ = x[MemoryKindForceExpression-168] + _ = x[MemoryKindPathExpression-169] + _ = x[MemoryKindAttachExpression-170] + _ = x[MemoryKindConstantSizedType-171] + _ = x[MemoryKindDictionaryType-172] + _ = x[MemoryKindFunctionType-173] + _ = x[MemoryKindInstantiationType-174] + _ = x[MemoryKindNominalType-175] + _ = x[MemoryKindOptionalType-176] + _ = x[MemoryKindReferenceType-177] + _ = x[MemoryKindIntersectionType-178] + _ = x[MemoryKindVariableSizedType-179] + _ = x[MemoryKindPosition-180] + _ = x[MemoryKindRange-181] + _ = x[MemoryKindElaboration-182] + _ = x[MemoryKindActivation-183] + _ = x[MemoryKindActivationEntries-184] + _ = x[MemoryKindVariableSizedSemaType-185] + _ = x[MemoryKindConstantSizedSemaType-186] + _ = x[MemoryKindDictionarySemaType-187] + _ = x[MemoryKindOptionalSemaType-188] + _ = x[MemoryKindIntersectionSemaType-189] + _ = x[MemoryKindReferenceSemaType-190] + _ = x[MemoryKindEntitlementSemaType-191] + _ = x[MemoryKindEntitlementMapSemaType-192] + _ = x[MemoryKindEntitlementRelationSemaType-193] + _ = x[MemoryKindCapabilitySemaType-194] + _ = x[MemoryKindInclusiveRangeSemaType-195] + _ = x[MemoryKindOrderedMap-196] + _ = x[MemoryKindOrderedMapEntryList-197] + _ = x[MemoryKindOrderedMapEntry-198] + _ = x[MemoryKindLast-199] } -const _MemoryKind_name = "UnknownAddressValueStringValueCharacterValueNumberValueArrayValueBaseDictionaryValueBaseCompositeValueBaseSimpleCompositeValueBaseOptionalValueTypeValuePathValueCapabilityValueStorageReferenceValueEphemeralReferenceValueInterpretedFunctionValueHostFunctionValueBoundFunctionValueBigIntSimpleCompositeValuePublishedValueStorageCapabilityControllerValueAccountCapabilityControllerValueAtreeArrayDataSlabAtreeArrayMetaDataSlabAtreeArrayElementOverheadAtreeMapDataSlabAtreeMapMetaDataSlabAtreeMapElementOverheadAtreeMapPreAllocatedElementAtreeEncodedSlabPrimitiveStaticTypeCompositeStaticTypeInterfaceStaticTypeVariableSizedStaticTypeConstantSizedStaticTypeDictionaryStaticTypeOptionalStaticTypeIntersectionStaticTypeEntitlementSetStaticAccessEntitlementMapStaticAccessReferenceStaticTypeCapabilityStaticTypeFunctionStaticTypeCadenceVoidValueCadenceOptionalValueCadenceBoolValueCadenceStringValueCadenceCharacterValueCadenceAddressValueCadenceIntValueCadenceNumberValueCadenceArrayValueBaseCadenceArrayValueLengthCadenceDictionaryValueCadenceKeyValuePairCadenceStructValueBaseCadenceStructValueSizeCadenceResourceValueBaseCadenceAttachmentValueBaseCadenceResourceValueSizeCadenceAttachmentValueSizeCadenceEventValueBaseCadenceEventValueSizeCadenceContractValueBaseCadenceContractValueSizeCadenceEnumValueBaseCadenceEnumValueSizeCadencePathValueCadenceTypeValueCadenceCapabilityValueCadenceFunctionValueCadenceOptionalTypeCadenceVariableSizedArrayTypeCadenceConstantSizedArrayTypeCadenceDictionaryTypeCadenceFieldCadenceParameterCadenceTypeParameterCadenceStructTypeCadenceResourceTypeCadenceAttachmentTypeCadenceEventTypeCadenceContractTypeCadenceStructInterfaceTypeCadenceResourceInterfaceTypeCadenceContractInterfaceTypeCadenceFunctionTypeCadenceEntitlementSetAccessCadenceEntitlementMapAccessCadenceReferenceTypeCadenceIntersectionTypeCadenceCapabilityTypeCadenceEnumTypeRawStringAddressLocationBytesVariableCompositeTypeInfoCompositeFieldInvocationStorageMapStorageKeyTypeTokenErrorTokenSpaceTokenProgramIdentifierArgumentBlockFunctionBlockParameterParameterListTypeParameterTypeParameterListTransferMembersTypeAnnotationDictionaryEntryFunctionDeclarationCompositeDeclarationAttachmentDeclarationInterfaceDeclarationEntitlementDeclarationEntitlementMappingElementEntitlementMappingDeclarationEnumCaseDeclarationFieldDeclarationTransactionDeclarationImportDeclarationVariableDeclarationSpecialFunctionDeclarationPragmaDeclarationAssignmentStatementBreakStatementContinueStatementEmitStatementExpressionStatementForStatementIfStatementReturnStatementSwapStatementSwitchStatementWhileStatementRemoveStatementBooleanExpressionVoidExpressionNilExpressionStringExpressionIntegerExpressionFixedPointExpressionArrayExpressionDictionaryExpressionIdentifierExpressionInvocationExpressionMemberExpressionIndexExpressionConditionalExpressionUnaryExpressionBinaryExpressionFunctionExpressionCastingExpressionCreateExpressionDestroyExpressionReferenceExpressionForceExpressionPathExpressionAttachExpressionConstantSizedTypeDictionaryTypeFunctionTypeInstantiationTypeNominalTypeOptionalTypeReferenceTypeIntersectionTypeVariableSizedTypePositionRangeElaborationActivationActivationEntriesVariableSizedSemaTypeConstantSizedSemaTypeDictionarySemaTypeOptionalSemaTypeIntersectionSemaTypeReferenceSemaTypeEntitlementSemaTypeEntitlementMapSemaTypeEntitlementRelationSemaTypeCapabilitySemaTypeOrderedMapOrderedMapEntryListOrderedMapEntryLast" +const _MemoryKind_name = "UnknownAddressValueStringValueCharacterValueNumberValueArrayValueBaseDictionaryValueBaseCompositeValueBaseSimpleCompositeValueBaseOptionalValueTypeValuePathValueCapabilityValueStorageReferenceValueEphemeralReferenceValueInterpretedFunctionValueHostFunctionValueBoundFunctionValueBigIntSimpleCompositeValuePublishedValueStorageCapabilityControllerValueAccountCapabilityControllerValueAtreeArrayDataSlabAtreeArrayMetaDataSlabAtreeArrayElementOverheadAtreeMapDataSlabAtreeMapMetaDataSlabAtreeMapElementOverheadAtreeMapPreAllocatedElementAtreeEncodedSlabPrimitiveStaticTypeCompositeStaticTypeInterfaceStaticTypeVariableSizedStaticTypeConstantSizedStaticTypeDictionaryStaticTypeInclusiveRangeStaticTypeOptionalStaticTypeIntersectionStaticTypeEntitlementSetStaticAccessEntitlementMapStaticAccessReferenceStaticTypeCapabilityStaticTypeFunctionStaticTypeCadenceVoidValueCadenceOptionalValueCadenceBoolValueCadenceStringValueCadenceCharacterValueCadenceAddressValueCadenceIntValueCadenceNumberValueCadenceArrayValueBaseCadenceArrayValueLengthCadenceDictionaryValueCadenceInclusiveRangeValueCadenceKeyValuePairCadenceStructValueBaseCadenceStructValueSizeCadenceResourceValueBaseCadenceAttachmentValueBaseCadenceResourceValueSizeCadenceAttachmentValueSizeCadenceEventValueBaseCadenceEventValueSizeCadenceContractValueBaseCadenceContractValueSizeCadenceEnumValueBaseCadenceEnumValueSizeCadencePathValueCadenceTypeValueCadenceCapabilityValueCadenceFunctionValueCadenceOptionalTypeCadenceVariableSizedArrayTypeCadenceConstantSizedArrayTypeCadenceDictionaryTypeCadenceInclusiveRangeTypeCadenceFieldCadenceParameterCadenceTypeParameterCadenceStructTypeCadenceResourceTypeCadenceAttachmentTypeCadenceEventTypeCadenceContractTypeCadenceStructInterfaceTypeCadenceResourceInterfaceTypeCadenceContractInterfaceTypeCadenceFunctionTypeCadenceEntitlementSetAccessCadenceEntitlementMapAccessCadenceReferenceTypeCadenceIntersectionTypeCadenceCapabilityTypeCadenceEnumTypeRawStringAddressLocationBytesVariableCompositeTypeInfoCompositeFieldInvocationStorageMapStorageKeyTypeTokenErrorTokenSpaceTokenProgramIdentifierArgumentBlockFunctionBlockParameterParameterListTypeParameterTypeParameterListTransferMembersTypeAnnotationDictionaryEntryFunctionDeclarationCompositeDeclarationAttachmentDeclarationInterfaceDeclarationEntitlementDeclarationEntitlementMappingElementEntitlementMappingDeclarationEnumCaseDeclarationFieldDeclarationTransactionDeclarationImportDeclarationVariableDeclarationSpecialFunctionDeclarationPragmaDeclarationAssignmentStatementBreakStatementContinueStatementEmitStatementExpressionStatementForStatementIfStatementReturnStatementSwapStatementSwitchStatementWhileStatementRemoveStatementBooleanExpressionVoidExpressionNilExpressionStringExpressionIntegerExpressionFixedPointExpressionArrayExpressionDictionaryExpressionIdentifierExpressionInvocationExpressionMemberExpressionIndexExpressionConditionalExpressionUnaryExpressionBinaryExpressionFunctionExpressionCastingExpressionCreateExpressionDestroyExpressionReferenceExpressionForceExpressionPathExpressionAttachExpressionConstantSizedTypeDictionaryTypeFunctionTypeInstantiationTypeNominalTypeOptionalTypeReferenceTypeIntersectionTypeVariableSizedTypePositionRangeElaborationActivationActivationEntriesVariableSizedSemaTypeConstantSizedSemaTypeDictionarySemaTypeOptionalSemaTypeIntersectionSemaTypeReferenceSemaTypeEntitlementSemaTypeEntitlementMapSemaTypeEntitlementRelationSemaTypeCapabilitySemaTypeInclusiveRangeSemaTypeOrderedMapOrderedMapEntryListOrderedMapEntryLast" -var _MemoryKind_index = [...]uint16{0, 7, 19, 30, 44, 55, 69, 88, 106, 130, 143, 152, 161, 176, 197, 220, 244, 261, 279, 285, 305, 319, 351, 383, 401, 423, 448, 464, 484, 507, 534, 550, 569, 588, 607, 630, 653, 673, 691, 713, 739, 765, 784, 804, 822, 838, 858, 874, 892, 913, 932, 947, 965, 986, 1009, 1031, 1050, 1072, 1094, 1118, 1144, 1168, 1194, 1215, 1236, 1260, 1284, 1304, 1324, 1340, 1356, 1378, 1398, 1417, 1446, 1475, 1496, 1508, 1524, 1544, 1561, 1580, 1601, 1617, 1636, 1662, 1690, 1718, 1737, 1764, 1791, 1811, 1834, 1855, 1870, 1879, 1894, 1899, 1907, 1924, 1938, 1948, 1958, 1968, 1977, 1987, 1997, 2004, 2014, 2022, 2027, 2040, 2049, 2062, 2075, 2092, 2100, 2107, 2121, 2136, 2155, 2175, 2196, 2216, 2238, 2263, 2292, 2311, 2327, 2349, 2366, 2385, 2411, 2428, 2447, 2461, 2478, 2491, 2510, 2522, 2533, 2548, 2561, 2576, 2590, 2605, 2622, 2636, 2649, 2665, 2682, 2702, 2717, 2737, 2757, 2777, 2793, 2808, 2829, 2844, 2860, 2878, 2895, 2911, 2928, 2947, 2962, 2976, 2992, 3009, 3023, 3035, 3052, 3063, 3075, 3088, 3104, 3121, 3129, 3134, 3145, 3155, 3172, 3193, 3214, 3232, 3248, 3268, 3285, 3304, 3326, 3353, 3371, 3381, 3400, 3415, 3419} +var _MemoryKind_index = [...]uint16{0, 7, 19, 30, 44, 55, 69, 88, 106, 130, 143, 152, 161, 176, 197, 220, 244, 261, 279, 285, 305, 319, 351, 383, 401, 423, 448, 464, 484, 507, 534, 550, 569, 588, 607, 630, 653, 673, 697, 715, 737, 763, 789, 808, 828, 846, 862, 882, 898, 916, 937, 956, 971, 989, 1010, 1033, 1055, 1081, 1100, 1122, 1144, 1168, 1194, 1218, 1244, 1265, 1286, 1310, 1334, 1354, 1374, 1390, 1406, 1428, 1448, 1467, 1496, 1525, 1546, 1571, 1583, 1599, 1619, 1636, 1655, 1676, 1692, 1711, 1737, 1765, 1793, 1812, 1839, 1866, 1886, 1909, 1930, 1945, 1954, 1969, 1974, 1982, 1999, 2013, 2023, 2033, 2043, 2052, 2062, 2072, 2079, 2089, 2097, 2102, 2115, 2124, 2137, 2150, 2167, 2175, 2182, 2196, 2211, 2230, 2250, 2271, 2291, 2313, 2338, 2367, 2386, 2402, 2424, 2441, 2460, 2486, 2503, 2522, 2536, 2553, 2566, 2585, 2597, 2608, 2623, 2636, 2651, 2665, 2680, 2697, 2711, 2724, 2740, 2757, 2777, 2792, 2812, 2832, 2852, 2868, 2883, 2904, 2919, 2935, 2953, 2970, 2986, 3003, 3022, 3037, 3051, 3067, 3084, 3098, 3110, 3127, 3138, 3150, 3163, 3179, 3196, 3204, 3209, 3220, 3230, 3247, 3268, 3289, 3307, 3323, 3343, 3360, 3379, 3401, 3428, 3446, 3468, 3478, 3497, 3512, 3516} func (i MemoryKind) String() string { if i >= MemoryKind(len(_MemoryKind_index)-1) { diff --git a/runtime/common/metering.go b/runtime/common/metering.go index de0879c8e3..529a03a7ef 100644 --- a/runtime/common/metering.go +++ b/runtime/common/metering.go @@ -184,7 +184,7 @@ var ( EntitlementMapSemaTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindEntitlementMapSemaType) EntitlementRelationSemaTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindEntitlementRelationSemaType) CapabilitySemaTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindCapabilitySemaType) - InclusiveRangeSemaTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindInclusiveRangeSemaType) + InclusiveRangeSemaTypeMemoryUsage = NewConstantMemoryUsage(MemoryKindInclusiveRangeSemaType) // Storage related memory usages @@ -269,7 +269,7 @@ var ( CapabilityStaticTypeStringMemoryUsage = NewRawStringMemoryUsage(12) // Capability<> IntersectionStaticTypeStringMemoryUsage = NewRawStringMemoryUsage(2) // {} IntersectionStaticTypeSeparatorStringMemoryUsage = NewRawStringMemoryUsage(2) // , - InclusiveRangeStaticTypeStringMemoryUsage = NewRawStringMemoryUsage(16) // InclusiveRange<> + InclusiveRangeStaticTypeStringMemoryUsage = NewRawStringMemoryUsage(16) // InclusiveRange<> ) func UseMemory(gauge MemoryGauge, usage MemoryUsage) { diff --git a/runtime/convertValues.go b/runtime/convertValues.go index d460e9ddb5..72212ac085 100644 --- a/runtime/convertValues.go +++ b/runtime/convertValues.go @@ -663,19 +663,6 @@ func exportCompositeValueAsInclusiveRange( return inclusiveRange.WithType(t), err } -func exportPathLinkValue(v interpreter.PathLinkValue, inter *interpreter.Interpreter) (cadence.PathLink, error) { - path, err := exportPathValue(inter, v.TargetPath) - if err != nil { - return cadence.PathLink{}, err - } - ty := string(inter.MustConvertStaticToSemaType(v.Type).ID()) - return cadence.NewMeteredPathLink(inter, path, ty), nil -} - -func exportAccountLinkValue(inter *interpreter.Interpreter) cadence.AccountLink { - return cadence.NewMeteredAccountLink(inter) -} - func exportPathValue(gauge common.MemoryGauge, v interpreter.PathValue) (cadence.Path, error) { return cadence.NewMeteredPath( gauge, diff --git a/runtime/convertValues_test.go b/runtime/convertValues_test.go index e0d8d3c6ea..572a0fb0d8 100644 --- a/runtime/convertValues_test.go +++ b/runtime/convertValues_test.go @@ -1515,20 +1515,19 @@ func TestExportInclusiveRangeValue(t *testing.T) { t.Parallel() script := ` - pub fun main(): InclusiveRange { + access(all) fun main(): InclusiveRange { return InclusiveRange(10, 20, step: 2) } ` - inclusiveRangeType := cadence.NewInclusiveRangeType(cadence.IntType{}) + inclusiveRangeType := cadence.NewInclusiveRangeType(cadence.IntType) - actual := cadence.ValueWithCachedTypeID(exportValueFromScript(t, script)) - expected := cadence.ValueWithCachedTypeID( - cadence.NewInclusiveRange( - cadence.NewInt(10), - cadence.NewInt(20), - cadence.NewInt(2)).WithType(inclusiveRangeType), - ) + actual := exportValueFromScript(t, script) + expected := cadence.NewInclusiveRange( + cadence.NewInt(10), + cadence.NewInt(20), + cadence.NewInt(2), + ).WithType(inclusiveRangeType) assert.Equal(t, expected, actual) }) @@ -1538,20 +1537,19 @@ func TestExportInclusiveRangeValue(t *testing.T) { t.Parallel() script := ` - pub fun main(): InclusiveRange { + access(all) fun main(): InclusiveRange { return InclusiveRange(10, 20) } ` - inclusiveRangeType := cadence.NewInclusiveRangeType(cadence.IntType{}) + inclusiveRangeType := cadence.NewInclusiveRangeType(cadence.IntType) - actual := cadence.ValueWithCachedTypeID(exportValueFromScript(t, script)) - expected := cadence.ValueWithCachedTypeID( - cadence.NewInclusiveRange( - cadence.NewInt(10), - cadence.NewInt(20), - cadence.NewInt(1)).WithType(inclusiveRangeType), - ) + actual := exportValueFromScript(t, script) + expected := cadence.NewInclusiveRange( + cadence.NewInt(10), + cadence.NewInt(20), + cadence.NewInt(1), + ).WithType(inclusiveRangeType) assert.Equal(t, expected, actual) }) @@ -1566,7 +1564,7 @@ func TestImportInclusiveRangeValue(t *testing.T) { value := cadence.NewInclusiveRange(cadence.NewInt(10), cadence.NewInt(-10), cadence.NewInt(-2)) - inter := newTestInterpreter(t) + inter := NewTestInterpreter(t) actual, err := ImportValue( inter, @@ -1600,7 +1598,7 @@ func TestImportInclusiveRangeValue(t *testing.T) { value := cadence.NewInclusiveRange(cadence.NewInt(10), cadence.NewInt(-10), cadence.NewInt(-2)) - inter := newTestInterpreter(t) + inter := NewTestInterpreter(t) actual, err := ImportValue( inter, @@ -1634,7 +1632,7 @@ func TestImportInclusiveRangeValue(t *testing.T) { value := cadence.NewInclusiveRange(cadence.NewInt(10), cadence.NewUInt(100), cadence.NewUInt64(1)) - inter := newTestInterpreter(t) + inter := NewTestInterpreter(t) _, err := ImportValue( inter, @@ -1664,7 +1662,7 @@ func TestImportInclusiveRangeValue(t *testing.T) { value := cadence.NewInclusiveRange(strValue, strValue, strValue) - inter := newTestInterpreter(t) + inter := NewTestInterpreter(t) _, err = ImportValue( inter, @@ -2762,7 +2760,7 @@ func TestRuntimeArgumentPassing(t *testing.T) { cadence.NewUInt128(500), cadence.NewUInt128(25), ).WithType(&cadence.InclusiveRangeType{ - ElementType: cadence.UInt128Type{}, + ElementType: cadence.UInt128Type, }), }, { diff --git a/runtime/imported_values_memory_metering_test.go b/runtime/imported_values_memory_metering_test.go index 39520c6003..eaad5f0d4b 100644 --- a/runtime/imported_values_memory_metering_test.go +++ b/runtime/imported_values_memory_metering_test.go @@ -417,13 +417,13 @@ func TestRuntimeImportedValueMemoryMetering(t *testing.T) { t.Parallel() script := []byte(` - pub fun main(x: InclusiveRange) {} + access(all) fun main(x: InclusiveRange) {} `) meter := make(map[common.MemoryKind]uint64) inclusiveRangeValue := &cadence.InclusiveRange{ InclusiveRangeType: &cadence.InclusiveRangeType{ - ElementType: cadence.IntType{}, + ElementType: cadence.IntType, }, Start: cadence.NewInt(1), End: cadence.NewInt(50), diff --git a/runtime/interpreter/statictype.go b/runtime/interpreter/statictype.go index a9cffc787b..c993c9b983 100644 --- a/runtime/interpreter/statictype.go +++ b/runtime/interpreter/statictype.go @@ -1166,7 +1166,14 @@ func ConvertStaticToSemaType( ), nil case InclusiveRangeStaticType: - elementType, err := ConvertStaticToSemaType(memoryGauge, t.ElementType, getInterface, getComposite) + elementType, err := ConvertStaticToSemaType( + memoryGauge, + t.ElementType, + getInterface, + getComposite, + getEntitlement, + getEntitlementMapType, + ) if err != nil { return nil, err } diff --git a/runtime/interpreter/statictype_test.go b/runtime/interpreter/statictype_test.go index 10c8080bd4..5f7cd1382a 100644 --- a/runtime/interpreter/statictype_test.go +++ b/runtime/interpreter/statictype_test.go @@ -730,7 +730,7 @@ func TestInclusiveRangeStaticType_Equal(t *testing.T) { InclusiveRangeStaticType{ ElementType: PrimitiveStaticTypeInt, }.Equal( - VariableSizedStaticType{ + &VariableSizedStaticType{ Type: PrimitiveStaticTypeInt, }, ), diff --git a/runtime/interpreter/value.go b/runtime/interpreter/value.go index 8e18504bce..2a475a9345 100644 --- a/runtime/interpreter/value.go +++ b/runtime/interpreter/value.go @@ -1598,11 +1598,11 @@ func (v *StringValue) ForEach( interpreter *Interpreter, _ sema.Type, function func(value Value) (resume bool), - _ LocationRange, + locationRange LocationRange, ) { - iterator := v.Iterator(interpreter) + iterator := v.Iterator(interpreter, locationRange) for { - value := iterator.Next(interpreter) + value := iterator.Next(interpreter, locationRange) if value == nil { return } @@ -16291,12 +16291,12 @@ func (UFix64Value) Scale() int { type FunctionOrderedMap = orderedmap.OrderedMap[string, FunctionValue] type CompositeValue struct { - Location common.Location + Location common.Location // note that the staticType is not guaranteed to be a CompositeStaticType as there can be types // which are non-composite but their values are treated as CompositeValue. // For e.g. InclusiveRangeValue - staticType StaticType + staticType StaticType Stringer func(gauge common.MemoryGauge, value *CompositeValue, seenReferences SeenReferences) string injectedFields map[string]Value @@ -17999,6 +17999,25 @@ func (v *CompositeValue) Iterator(interpreter *Interpreter, locationRange Locati } } +func (v *CompositeValue) ForEach( + interpreter *Interpreter, + _ sema.Type, + function func(value Value) (resume bool), + locationRange LocationRange, +) { + iterator := v.Iterator(interpreter, locationRange) + for { + value := iterator.Next(interpreter, locationRange) + if value == nil { + return + } + + if !function(value) { + return + } + } +} + type InclusiveRangeIterator struct { rangeValue *CompositeValue next IntegerValue @@ -20268,7 +20287,7 @@ func (*StorageReferenceValue) DeepRemove(_ *Interpreter) { func (*StorageReferenceValue) isReference() {} -func (v *StorageReferenceValue) Iterator(_ *Interpreter) ValueIterator { +func (v *StorageReferenceValue) Iterator(_ *Interpreter, _ LocationRange) ValueIterator { // Not used for now panic(errors.NewUnreachableError()) } @@ -20632,7 +20651,7 @@ func (*EphemeralReferenceValue) DeepRemove(_ *Interpreter) { func (*EphemeralReferenceValue) isReference() {} -func (v *EphemeralReferenceValue) Iterator(_ *Interpreter) ValueIterator { +func (v *EphemeralReferenceValue) Iterator(_ *Interpreter, _ LocationRange) ValueIterator { // Not used for now panic(errors.NewUnreachableError()) } diff --git a/runtime/interpreter/value_range.go b/runtime/interpreter/value_range.go index 566cef02d2..4174e76440 100644 --- a/runtime/interpreter/value_range.go +++ b/runtime/interpreter/value_range.go @@ -22,6 +22,7 @@ import ( "fmt" "github.com/onflow/cadence/runtime/common" + "github.com/onflow/cadence/runtime/common/orderedmap" "github.com/onflow/cadence/runtime/errors" "github.com/onflow/cadence/runtime/sema" ) @@ -159,8 +160,11 @@ func createInclusiveRange( rangeType, ) - rangeValue.Functions = map[string]FunctionValue{ - sema.InclusiveRangeTypeContainsFunctionName: NewHostFunctionValue( + rangeValue.Functions = orderedmap.New[FunctionOrderedMap](1) + + rangeValue.Functions.Set( + sema.InclusiveRangeTypeContainsFunctionName, + NewHostFunctionValue( interpreter, sema.InclusiveRangeContainsFunctionType( rangeSemaType.MemberType, @@ -177,7 +181,7 @@ func createInclusiveRange( ) }, ), - } + ) return rangeValue } diff --git a/runtime/program_params_validation_test.go b/runtime/program_params_validation_test.go index b8c6b0c8d3..f9a2b693db 100644 --- a/runtime/program_params_validation_test.go +++ b/runtime/program_params_validation_test.go @@ -296,7 +296,7 @@ func TestRuntimeScriptParameterTypeValidation(t *testing.T) { t.Parallel() script := ` - pub fun main(arg: InclusiveRange) { + access(all) fun main(arg: InclusiveRange) { } ` @@ -313,7 +313,7 @@ func TestRuntimeScriptParameterTypeValidation(t *testing.T) { t.Parallel() script := ` - pub fun main(arg: AnyStruct) { + access(all) fun main(arg: AnyStruct) { } ` @@ -334,7 +334,7 @@ func TestRuntimeScriptParameterTypeValidation(t *testing.T) { t.Parallel() script := ` - pub fun main(arg: InclusiveRange) { + access(all) fun main(arg: InclusiveRange) { } ` @@ -355,7 +355,7 @@ func TestRuntimeScriptParameterTypeValidation(t *testing.T) { t.Parallel() script := ` - pub fun main(arg: InclusiveRange) { + access(all) fun main(arg: InclusiveRange) { } ` @@ -373,7 +373,7 @@ func TestRuntimeScriptParameterTypeValidation(t *testing.T) { t.Parallel() script := ` - pub fun main(arg: InclusiveRange) { + access(all) fun main(arg: InclusiveRange) { } ` diff --git a/runtime/runtime_test.go b/runtime/runtime_test.go index c2fb827e22..2d713abda7 100644 --- a/runtime/runtime_test.go +++ b/runtime/runtime_test.go @@ -1680,10 +1680,10 @@ func TestRuntimeStorageMultipleTransactionsInclusiveRangeFunction(t *testing.T) t.Parallel() - runtime := newTestInterpreterRuntime() + runtime := NewTestInterpreterRuntime() inclusiveRangeCreation := []byte(` - pub fun createInclusiveRange(): InclusiveRange { + access(all) fun createInclusiveRange(): InclusiveRange { return InclusiveRange(10, 20) } `) @@ -1691,17 +1691,17 @@ func TestRuntimeStorageMultipleTransactionsInclusiveRangeFunction(t *testing.T) script1 := []byte(` import "inclusive-range-creation" transaction { - prepare(signer: AuthAccount) { + prepare(signer: auth(Storage) &Account) { let ir = createInclusiveRange() - signer.save(ir, to: /storage/inclusiveRange) + signer.storage.save(ir, to: /storage/inclusiveRange) } } `) - ledger := newTestLedger(nil, nil) + ledger := NewTestLedger(nil, nil) - runtimeInterface := &testRuntimeInterface{ - getCode: func(location Location) (bytes []byte, err error) { + runtimeInterface := &TestRuntimeInterface{ + OnGetCode: func(location Location) (bytes []byte, err error) { switch location { case common.StringLocation("inclusive-range-creation"): return inclusiveRangeCreation, nil @@ -1709,13 +1709,13 @@ func TestRuntimeStorageMultipleTransactionsInclusiveRangeFunction(t *testing.T) return nil, fmt.Errorf("unknown import location: %s", location) } }, - storage: ledger, - getSigningAccounts: func() ([]Address, error) { + Storage: ledger, + OnGetSigningAccounts: func() ([]Address, error) { return []Address{{42}}, nil }, } - nextTransactionLocation := newTransactionLocationGenerator() + nextTransactionLocation := NewTransactionLocationGenerator() err := runtime.ExecuteTransaction( Script{ @@ -8909,6 +8909,10 @@ func TestRuntimeTypesAndConversions(t *testing.T) { } for name, ty := range checker.AllBaseSemaTypes() { + // Inclusive range is a dynamically created type. + if _, isInclusiveRange := ty.(*sema.InclusiveRangeType); isInclusiveRange { + continue + } test(name, ty) } } diff --git a/runtime/sema/type.go b/runtime/sema/type.go index 7ffc4c2b8b..6c540641a7 100644 --- a/runtime/sema/type.go +++ b/runtime/sema/type.go @@ -1027,7 +1027,9 @@ func (t *GenericType) GetMembers() map[string]MemberResolver { } func (t *GenericType) CheckInstantiated(pos ast.HasPosition, memoryGauge common.MemoryGauge, report func(err error)) { - t.TypeParameter.TypeBound.CheckInstantiated(pos, memoryGauge, report) + if t.TypeParameter.TypeBound != nil { + t.TypeParameter.TypeBound.CheckInstantiated(pos, memoryGauge, report) + } } // IntegerRangedType @@ -3990,15 +3992,13 @@ var AllUnsignedIntegerTypes = common.Concat( var AllNonLeafIntegerTypes = []Type{ IntegerType, SignedIntegerType, + FixedSizeUnsignedIntegerType, } var AllIntegerTypes = common.Concat( AllUnsignedIntegerTypes, AllSignedIntegerTypes, AllNonLeafIntegerTypes, - []Type{ - FixedSizeUnsignedIntegerType, - }, ) var AllNumberTypes = common.Concat( @@ -5006,10 +5006,6 @@ func (t *CompositeType) CheckInstantiated(pos ast.HasPosition, memoryGauge commo t.baseType.CheckInstantiated(pos, memoryGauge, report) } - for _, typ := range t.ImplicitTypeRequirementConformances { - typ.CheckInstantiated(pos, memoryGauge, report) - } - for _, typ := range t.ExplicitInterfaceConformances { typ.CheckInstantiated(pos, memoryGauge, report) } @@ -6262,11 +6258,11 @@ func (t *InclusiveRangeType) TypeAnnotationState() TypeAnnotationState { return t.MemberType.TypeAnnotationState() } -func (t *InclusiveRangeType) RewriteWithRestrictedTypes() (Type, bool) { +func (t *InclusiveRangeType) RewriteWithIntersectionTypes() (Type, bool) { if t.MemberType == nil { return t, false } - rewrittenMemberType, rewritten := t.MemberType.RewriteWithRestrictedTypes() + rewrittenMemberType, rewritten := t.MemberType.RewriteWithIntersectionTypes() if rewritten { return &InclusiveRangeType{ MemberType: rewrittenMemberType, @@ -6465,6 +6461,23 @@ func (t *InclusiveRangeType) Resolve(typeArguments *TypeParameterTypeOrderedMap) } } +func (t *InclusiveRangeType) IsPrimitiveType() bool { + return false +} + +func (t *InclusiveRangeType) ContainFieldsOrElements() bool { + return false +} + +func (t *InclusiveRangeType) Map( + gauge common.MemoryGauge, + typeParamMap map[*TypeParameter]*TypeParameter, + f func(Type) Type, +) Type { + mappedMemberType := t.MemberType.Map(gauge, typeParamMap, f) + return f(NewInclusiveRangeType(gauge, mappedMemberType)) +} + // ReferenceType represents the reference to a value type ReferenceType struct { Type Type @@ -7794,8 +7807,8 @@ func (t *IntersectionType) IsValidIndexingType(ty Type) bool { attachmentType.IsResourceType() == t.IsResourceType() } -func (t *IntersectionType) CheckInstantiated(pos ast.HasPosition, memoryGauge common.MemoryGauge, report func(err error)) { - t.Type.CheckInstantiated(pos, memoryGauge, report) +func (t *IntersectionType) CheckInstantiated(_ ast.HasPosition, _ common.MemoryGauge, _ func(err error)) { + // No-OP } // CapabilityType @@ -8508,6 +8521,10 @@ func (t *EntitlementType) Resolve(_ *TypeParameterTypeOrderedMap) Type { return t } +func (t *EntitlementType) CheckInstantiated(_ ast.HasPosition, _ common.MemoryGauge, _ func(err error)) { + // No-OP +} + // EntitlementMapType type EntitlementRelation struct { @@ -8724,6 +8741,10 @@ func (t *EntitlementMapType) resolveEntitlementMappingInclusions( }) } +func (t *EntitlementMapType) CheckInstantiated(_ ast.HasPosition, _ common.MemoryGauge, _ func(err error)) { + // NO-OP +} + var NativeCompositeTypes = map[string]*CompositeType{} func init() { diff --git a/runtime/tests/checker/for_test.go b/runtime/tests/checker/for_test.go index a84fbd180a..495d30fe7b 100644 --- a/runtime/tests/checker/for_test.go +++ b/runtime/tests/checker/for_test.go @@ -25,6 +25,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/onflow/cadence/runtime/common" "github.com/onflow/cadence/runtime/sema" "github.com/onflow/cadence/runtime/stdlib" ) @@ -89,7 +90,9 @@ func TestCheckForInclusiveRange(t *testing.T) { for _, typ := range sema.AllIntegerTypes { // Only test leaf integer types switch typ { - case sema.IntegerType, sema.SignedIntegerType: + case sema.IntegerType, + sema.SignedIntegerType, + sema.FixedSizeUnsignedIntegerType: continue } @@ -109,7 +112,9 @@ func TestCheckForInclusiveRange(t *testing.T) { _, err := ParseAndCheckWithOptions(t, code, ParseAndCheckOptions{ Config: &sema.Config{ - BaseValueActivation: baseValueActivation, + BaseValueActivationHandler: func(common.Location) *sema.VariableActivation { + return baseValueActivation + }, }, }, ) diff --git a/runtime/tests/checker/operations_test.go b/runtime/tests/checker/operations_test.go index 94606cc2f5..38c02bcc7f 100644 --- a/runtime/tests/checker/operations_test.go +++ b/runtime/tests/checker/operations_test.go @@ -403,7 +403,9 @@ func TestCheckNonIntegerComparisonOperations(t *testing.T) { ), ParseAndCheckOptions{ Config: &sema.Config{ - BaseValueActivation: baseValueActivation, + BaseValueActivationHandler: func(common.Location) *sema.VariableActivation { + return baseValueActivation + }, }, }, ) diff --git a/runtime/tests/checker/range_value_test.go b/runtime/tests/checker/range_value_test.go index ae6ae2b379..5bc94600b3 100644 --- a/runtime/tests/checker/range_value_test.go +++ b/runtime/tests/checker/range_value_test.go @@ -25,6 +25,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/onflow/cadence/runtime/common" "github.com/onflow/cadence/runtime/sema" "github.com/onflow/cadence/runtime/stdlib" ) @@ -247,7 +248,9 @@ func TestCheckInclusiveRangeConstructionValid(t *testing.T) { checker, err := ParseAndCheckWithOptions(t, code, ParseAndCheckOptions{ Config: &sema.Config{ - BaseValueActivation: baseValueActivation, + BaseValueActivationHandler: func(common.Location) *sema.VariableActivation { + return baseValueActivation + }, }, }, ) @@ -289,7 +292,9 @@ func TestCheckInclusiveRangeConstructionInvalid(t *testing.T) { _, err := ParseAndCheckWithOptions(t, code, ParseAndCheckOptions{ Config: &sema.Config{ - BaseValueActivation: baseValueActivation, + BaseValueActivationHandler: func(common.Location) *sema.VariableActivation { + return baseValueActivation + }, }, }, ) @@ -304,7 +309,9 @@ func TestCheckInclusiveRangeConstructionInvalid(t *testing.T) { for _, integerType := range sema.AllIntegerTypes { // Only test leaf types switch integerType { - case sema.IntegerType, sema.SignedIntegerType: + case sema.IntegerType, + sema.SignedIntegerType, + sema.FixedSizeUnsignedIntegerType: continue } @@ -410,13 +417,15 @@ func TestInclusiveRangeNonLeafIntegerTypes(t *testing.T) { return ParseAndCheckOptions{ Config: &sema.Config{ - BaseValueActivation: baseValueActivation, + BaseValueActivationHandler: func(common.Location) *sema.VariableActivation { + return baseValueActivation + }, }, } } test := func(t *testing.T, ty sema.Type) { - t.Run(fmt.Sprintf("InclusiveRange<%s>", ty), func(t *testing.T) { + t.Run(fmt.Sprintf("InclusiveRange<%s> infer from args", ty), func(t *testing.T) { t.Parallel() _, err := ParseAndCheckWithOptions(t, fmt.Sprintf(` @@ -429,7 +438,7 @@ func TestInclusiveRangeNonLeafIntegerTypes(t *testing.T) { assert.IsType(t, &sema.InvalidTypeArgumentError{}, errs[0]) }) - t.Run(fmt.Sprintf("InclusiveRange<%s>", ty), func(t *testing.T) { + t.Run(fmt.Sprintf("InclusiveRange<%s> infer from lhs", ty), func(t *testing.T) { t.Parallel() _, err := ParseAndCheckWithOptions(t, fmt.Sprintf(` @@ -457,7 +466,10 @@ func TestInclusiveRangeNonLeafIntegerTypes(t *testing.T) { }) } - for _, ty := range sema.AllNonLeafIntegerTypes { + for _, ty := range []sema.Type{ + sema.IntegerType, + sema.SignedIntegerType, + } { test(t, ty) } } diff --git a/runtime/tests/checker/typeargument_test.go b/runtime/tests/checker/typeargument_test.go index 1c75a29eee..060c983961 100644 --- a/runtime/tests/checker/typeargument_test.go +++ b/runtime/tests/checker/typeargument_test.go @@ -68,7 +68,9 @@ func TestCheckTypeArguments(t *testing.T) { `, ParseAndCheckOptions{ Config: &sema.Config{ - BaseValueActivation: baseValueActivation, + BaseValueActivationHandler: func(_ common.Location) *sema.VariableActivation { + return baseValueActivation + }, }, }, ) @@ -167,7 +169,9 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { baseValueActivation.DeclareValue(stdlib.InclusiveRangeConstructorFunction) options := ParseAndCheckOptions{ Config: &sema.Config{ - BaseValueActivation: baseValueActivation, + BaseValueActivationHandler: func(_ common.Location) *sema.VariableActivation { + return baseValueActivation + }, }, } @@ -456,10 +460,10 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { err := test(t, ` struct Bar { - let f: ((): InclusiveRange) + let f: (fun(): InclusiveRange) init() { - self.f = fun () : InclusiveRange { + self.f = fun(): InclusiveRange { return InclusiveRange(1, 10) } } @@ -477,10 +481,10 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { err := test(t, ` struct Bar { - let f: ((): InclusiveRange) + let f: (fun(): InclusiveRange) init() { - self.f = fun () : InclusiveRange { + self.f = fun(): InclusiveRange { return InclusiveRange(1, 10) } } @@ -623,10 +627,6 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { init(b : @Bar) { self.bar <- b } - - destroy() { - destroy self.bar - } } `, ) @@ -654,10 +654,6 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { init(b : @Bar) { self.bar <- b } - - destroy() { - destroy self.bar - } } `, ) @@ -741,7 +737,7 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { err := test(t, ` - pub fun main(): Type { + access(all) fun main(): Type { return Type>() } `, @@ -756,7 +752,7 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { err := test(t, ` - pub fun main(): Type { + access(all) fun main(): Type { return Type() } `, @@ -800,15 +796,15 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { err := test(t, ` - pub fun main(): Direction { + access(all) fun main(): Direction { return Direction.RIGHT } - pub enum Direction: Int { - pub case UP - pub case DOWN - pub case LEFT - pub case RIGHT + access(all) enum Direction: Int { + access(all) case UP + access(all) case DOWN + access(all) case LEFT + access(all) case RIGHT } `, ) @@ -826,7 +822,9 @@ func TestCheckParameterizedTypeIsInstantiated(t *testing.T) { options := ParseAndCheckOptions{ Config: &sema.Config{ - BaseTypeActivation: baseTypeActivation, + BaseTypeActivationHandler: func(_ common.Location) *sema.VariableActivation { + return baseTypeActivation + }, }, } diff --git a/runtime/tests/interpreter/dynamic_casting_test.go b/runtime/tests/interpreter/dynamic_casting_test.go index 3c5d0c1946..119b22458b 100644 --- a/runtime/tests/interpreter/dynamic_casting_test.go +++ b/runtime/tests/interpreter/dynamic_casting_test.go @@ -1479,10 +1479,14 @@ func TestInterpretDynamicCastingInclusiveRange(t *testing.T) { options := ParseCheckAndInterpretOptions{ CheckerConfig: &sema.Config{ - BaseValueActivation: baseValueActivation, + BaseValueActivationHandler: func(common.Location) *sema.VariableActivation { + return baseValueActivation + }, }, Config: &interpreter.Config{ - BaseActivation: baseActivation, + BaseActivationHandler: func(common.Location) *interpreter.VariableActivation { + return baseActivation + }, }, } diff --git a/runtime/tests/interpreter/for_test.go b/runtime/tests/interpreter/for_test.go index 01fa7fb316..6763c8f89c 100644 --- a/runtime/tests/interpreter/for_test.go +++ b/runtime/tests/interpreter/for_test.go @@ -805,10 +805,14 @@ func TestInclusiveRangeForInLoop(t *testing.T) { inter, err := parseCheckAndInterpretWithOptions(t, code, ParseCheckAndInterpretOptions{ CheckerConfig: &sema.Config{ - BaseValueActivation: baseValueActivation, + BaseValueActivationHandler: func(common.Location) *sema.VariableActivation { + return baseValueActivation + }, }, Config: &interpreter.Config{ - BaseActivation: baseActivation, + BaseActivationHandler: func(common.Location) *interpreter.VariableActivation { + return baseActivation + }, }, }, ) @@ -850,7 +854,9 @@ func TestInclusiveRangeForInLoop(t *testing.T) { for _, typ := range sema.AllIntegerTypes { // Only test leaf types switch typ { - case sema.IntegerType, sema.SignedIntegerType: + case sema.IntegerType, + sema.SignedIntegerType, + sema.FixedSizeUnsignedIntegerType: continue } diff --git a/runtime/tests/interpreter/interface_test.go b/runtime/tests/interpreter/interface_test.go index a66b429611..ebaf634612 100644 --- a/runtime/tests/interpreter/interface_test.go +++ b/runtime/tests/interpreter/interface_test.go @@ -653,7 +653,7 @@ func TestInterpretInterfaceFunctionConditionsInheritance(t *testing.T) { }, }, Config: &interpreter.Config{ - BaseActivationHandler: func(location common.Location) *interpreter.VariableActivation { + BaseActivationHandler: func(common.Location) *interpreter.VariableActivation { return baseActivation }, }, @@ -765,7 +765,7 @@ func TestInterpretInterfaceFunctionConditionsInheritance(t *testing.T) { }, }, Config: &interpreter.Config{ - BaseActivationHandler: func(location common.Location) *interpreter.VariableActivation { + BaseActivationHandler: func(common.Location) *interpreter.VariableActivation { return baseActivation }, }, @@ -856,7 +856,7 @@ func TestInterpretInterfaceFunctionConditionsInheritance(t *testing.T) { }, }, Config: &interpreter.Config{ - BaseActivationHandler: func(location common.Location) *interpreter.VariableActivation { + BaseActivationHandler: func(common.Location) *interpreter.VariableActivation { return baseActivation }, ContractValueHandler: makeContractValueHandler(nil, nil, nil), diff --git a/runtime/tests/interpreter/range_value_test.go b/runtime/tests/interpreter/range_value_test.go index 8f26b2fa15..93bb45c80b 100644 --- a/runtime/tests/interpreter/range_value_test.go +++ b/runtime/tests/interpreter/range_value_test.go @@ -26,6 +26,7 @@ import ( "github.com/stretchr/testify/require" "github.com/onflow/cadence/runtime/activations" + "github.com/onflow/cadence/runtime/common" "github.com/onflow/cadence/runtime/interpreter" "github.com/onflow/cadence/runtime/sema" "github.com/onflow/cadence/runtime/stdlib" @@ -394,10 +395,14 @@ func TestInclusiveRange(t *testing.T) { inter, err := parseCheckAndInterpretWithOptions(t, code, ParseCheckAndInterpretOptions{ CheckerConfig: &sema.Config{ - BaseValueActivation: baseValueActivation, + BaseValueActivationHandler: func(common.Location) *sema.VariableActivation { + return baseValueActivation + }, }, Config: &interpreter.Config{ - BaseActivation: baseActivation, + BaseActivationHandler: func(common.Location) *interpreter.VariableActivation { + return baseActivation + }, }, }, ) @@ -476,7 +481,9 @@ func TestGetValueForIntegerType(t *testing.T) { for _, integerType := range sema.AllIntegerTypes { switch integerType { - case sema.IntegerType, sema.SignedIntegerType: + case sema.IntegerType, + sema.SignedIntegerType, + sema.FixedSizeUnsignedIntegerType: continue } @@ -503,10 +510,14 @@ func TestInclusiveRangeConstructionInvalid(t *testing.T) { _, err := parseCheckAndInterpretWithOptions(t, code, ParseCheckAndInterpretOptions{ CheckerConfig: &sema.Config{ - BaseValueActivation: baseValueActivation, + BaseValueActivationHandler: func(common.Location) *sema.VariableActivation { + return baseValueActivation + }, }, Config: &interpreter.Config{ - BaseActivation: baseActivation, + BaseActivationHandler: func(common.Location) *interpreter.VariableActivation { + return baseActivation + }, }, }, ) @@ -521,7 +532,9 @@ func TestInclusiveRangeConstructionInvalid(t *testing.T) { for _, integerType := range sema.AllIntegerTypes { // Only test leaf types switch integerType { - case sema.IntegerType, sema.SignedIntegerType: + case sema.IntegerType, + sema.SignedIntegerType, + sema.FixedSizeUnsignedIntegerType: continue } diff --git a/values.go b/values.go index ad4c902595..01b893706e 100644 --- a/values.go +++ b/values.go @@ -2071,7 +2071,6 @@ func (v Contract) GetFieldValues() []Value { return v.Fields } - // InclusiveRange type InclusiveRange struct { diff --git a/values_test.go b/values_test.go index d43dc96db8..29ed541ad6 100644 --- a/values_test.go +++ b/values_test.go @@ -223,7 +223,7 @@ func newValueTestCases() map[string]valueTestCase { }, "InclusiveRange": { value: NewInclusiveRange(NewInt(85), NewInt(-85), NewInt(-2)), - exampleType: NewInclusiveRangeType(IntType{}), + exampleType: NewInclusiveRangeType(IntType), withType: func(value Value, ty Type) Value { return value.(*InclusiveRange).WithType(ty.(*InclusiveRangeType)) }, From 4825f92c04d0135f5230db833074e88d7bf577a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Tue, 9 Jan 2024 14:35:41 -0800 Subject: [PATCH 121/121] improve type inference: use bound type parameters for argument expected type --- runtime/sema/check_invocation_expression.go | 42 ++++- runtime/sema/checker.go | 3 +- runtime/sema/simple_type.go | 8 +- runtime/sema/type.go | 178 +++++++++++++++--- runtime/stdlib/test_test.go | 18 +- runtime/tests/checker/account_test.go | 20 +- .../tests/checker/builtinfunctions_test.go | 26 ++- runtime/tests/checker/genericfunction_test.go | 10 +- runtime/tests/checker/range_value_test.go | 8 +- runtime/tests/checker/type_inference_test.go | 156 ++++++++++++--- 10 files changed, 356 insertions(+), 113 deletions(-) diff --git a/runtime/sema/check_invocation_expression.go b/runtime/sema/check_invocation_expression.go index db4d5c64c9..df25379fad 100644 --- a/runtime/sema/check_invocation_expression.go +++ b/runtime/sema/check_invocation_expression.go @@ -578,21 +578,46 @@ func (checker *Checker) checkInvocationRequiredArgument( var argumentType Type - if len(functionType.TypeParameters) == 0 { - // If the function doesn't use generic types, then the - // param types can be used to infer the types for arguments. + typeParameterCount := len(functionType.TypeParameters) + + // If all type parameters have been bound to a type, + // then resolve the parameter type with the type arguments, + // and propose the parameter type as the expected type for the argument. + if typeParameters.Len() == typeParameterCount { + + // Optimization: only resolve if there are type parameters. + // This avoids unnecessary work for non-generic functions. + if typeParameterCount > 0 { + parameterType = parameterType.Resolve(typeParameters) + // If the type parameter could not be resolved, use the invalid type. + if parameterType == nil { + parameterType = InvalidType + } + } + argumentType = checker.VisitExpression(argument.Expression, parameterType) + } else { - // TODO: pass the expected type to support for parameters + // If there are still type parameters that have not been bound to a type, + // then check the argument without an expected type. + // + // We will then have to manually check that the argument type is compatible + // with the parameter type (see below). + argumentType = checker.VisitExpression(argument.Expression, nil) // Try to unify the parameter type with the argument type. // If unification fails, fall back to the parameter type for now. - argumentRange := ast.NewRangeFromPositioned(checker.memoryGauge, argument.Expression) - - if parameterType.Unify(argumentType, typeParameters, checker.report, argumentRange) { + if parameterType.Unify( + argumentType, + typeParameters, + checker.report, + checker.memoryGauge, + argument.Expression, + ) { parameterType = parameterType.Resolve(typeParameters) + // If the type parameter could not be resolved, use the invalid type. if parameterType == nil { parameterType = InvalidType } @@ -600,7 +625,6 @@ func (checker *Checker) checkInvocationRequiredArgument( // Check that the type of the argument matches the type of the parameter. - // TODO: remove this once type inferring support for parameters is added checker.checkInvocationArgumentParameterTypeCompatibility( argument.Expression, argumentType, @@ -695,7 +719,7 @@ func (checker *Checker) checkAndBindGenericTypeParameterTypeArguments( // If the type parameter corresponding to the type argument has a type bound, // then check that the argument is a subtype of the type bound. - err := typeParameter.checkTypeBound(ty, ast.NewRangeFromPositioned(checker.memoryGauge, rawTypeArgument)) + err := typeParameter.checkTypeBound(ty, checker.memoryGauge, rawTypeArgument) checker.report(err) // Bind the type argument to the type parameter diff --git a/runtime/sema/checker.go b/runtime/sema/checker.go index 2df2c3daf4..c1331d1044 100644 --- a/runtime/sema/checker.go +++ b/runtime/sema/checker.go @@ -2475,7 +2475,8 @@ func (checker *Checker) convertInstantiationType(t *ast.InstantiationType) Type err := typeParameter.checkTypeBound( typeArgument, - ast.NewRangeFromPositioned(checker.memoryGauge, rawTypeArgument), + checker.memoryGauge, + rawTypeArgument, ) checker.report(err) } diff --git a/runtime/sema/simple_type.go b/runtime/sema/simple_type.go index d102815a9c..9f79f305aa 100644 --- a/runtime/sema/simple_type.go +++ b/runtime/sema/simple_type.go @@ -123,7 +123,13 @@ func (t *SimpleType) RewriteWithIntersectionTypes() (Type, bool) { return t, false } -func (*SimpleType) Unify(_ Type, _ *TypeParameterTypeOrderedMap, _ func(err error), _ ast.Range) bool { +func (*SimpleType) Unify( + _ Type, + _ *TypeParameterTypeOrderedMap, + _ func(err error), + _ common.MemoryGauge, + _ ast.HasPosition, +) bool { return false } diff --git a/runtime/sema/type.go b/runtime/sema/type.go index 6c540641a7..f59e220c5c 100644 --- a/runtime/sema/type.go +++ b/runtime/sema/type.go @@ -191,7 +191,8 @@ type Type interface { other Type, typeParameters *TypeParameterTypeOrderedMap, report func(err error), - outerRange ast.Range, + memoryGauge common.MemoryGauge, + outerRange ast.HasPosition, ) bool // Resolve returns a type that is free of generic types (see `GenericType`), @@ -758,7 +759,8 @@ func (t *OptionalType) Unify( other Type, typeParameters *TypeParameterTypeOrderedMap, report func(err error), - outerRange ast.Range, + memoryGauge common.MemoryGauge, + outerRange ast.HasPosition, ) bool { otherOptional, ok := other.(*OptionalType) @@ -766,7 +768,13 @@ func (t *OptionalType) Unify( return false } - return t.Type.Unify(otherOptional.Type, typeParameters, report, outerRange) + return t.Type.Unify( + otherOptional.Type, + typeParameters, + report, + memoryGauge, + outerRange, + ) } func (t *OptionalType) Resolve(typeArguments *TypeParameterTypeOrderedMap) Type { @@ -968,7 +976,8 @@ func (t *GenericType) Unify( other Type, typeParameters *TypeParameterTypeOrderedMap, report func(err error), - outerRange ast.Range, + memoryGauge common.MemoryGauge, + outerRange ast.HasPosition, ) bool { if unifiedType, ok := typeParameters.Get(t.TypeParameter); ok { @@ -983,7 +992,7 @@ func (t *GenericType) Unify( TypeParameter: t.TypeParameter, ExpectedType: unifiedType, ActualType: other, - Range: outerRange, + Range: ast.NewRangeFromPositioned(memoryGauge, outerRange), }, ) } @@ -996,7 +1005,7 @@ func (t *GenericType) Unify( // If the type parameter corresponding to the type argument has a type bound, // then check that the argument's type is a subtype of the type bound. - err := t.TypeParameter.checkTypeBound(other, outerRange) + err := t.TypeParameter.checkTypeBound(other, memoryGauge, outerRange) if err != nil { report(err) } @@ -1282,7 +1291,13 @@ func (t *NumericType) MaxInt() *big.Int { return t.maxInt } -func (*NumericType) Unify(_ Type, _ *TypeParameterTypeOrderedMap, _ func(err error), _ ast.Range) bool { +func (*NumericType) Unify( + _ Type, + _ *TypeParameterTypeOrderedMap, + _ func(err error), + _ common.MemoryGauge, + _ ast.HasPosition, +) bool { return false } @@ -1491,7 +1506,13 @@ func (t *FixedPointNumericType) Scale() uint { return t.scale } -func (*FixedPointNumericType) Unify(_ Type, _ *TypeParameterTypeOrderedMap, _ func(err error), _ ast.Range) bool { +func (*FixedPointNumericType) Unify( + _ Type, + _ *TypeParameterTypeOrderedMap, + _ func(err error), + _ common.MemoryGauge, + _ ast.HasPosition, +) bool { return false } @@ -2806,7 +2827,8 @@ func (t *VariableSizedType) Unify( other Type, typeParameters *TypeParameterTypeOrderedMap, report func(err error), - outerRange ast.Range, + memoryGauge common.MemoryGauge, + outerRange ast.HasPosition, ) bool { otherArray, ok := other.(*VariableSizedType) @@ -2814,7 +2836,13 @@ func (t *VariableSizedType) Unify( return false } - return t.Type.Unify(otherArray.Type, typeParameters, report, outerRange) + return t.Type.Unify( + otherArray.Type, + typeParameters, + report, + memoryGauge, + outerRange, + ) } func (t *VariableSizedType) Resolve(typeArguments *TypeParameterTypeOrderedMap) Type { @@ -2986,7 +3014,8 @@ func (t *ConstantSizedType) Unify( other Type, typeParameters *TypeParameterTypeOrderedMap, report func(err error), - outerRange ast.Range, + memoryGauge common.MemoryGauge, + outerRange ast.HasPosition, ) bool { otherArray, ok := other.(*ConstantSizedType) @@ -2998,7 +3027,13 @@ func (t *ConstantSizedType) Unify( return false } - return t.Type.Unify(otherArray.Type, typeParameters, report, outerRange) + return t.Type.Unify( + otherArray.Type, + typeParameters, + report, + memoryGauge, + outerRange, + ) } func (t *ConstantSizedType) Resolve(typeArguments *TypeParameterTypeOrderedMap) Type { @@ -3132,7 +3167,7 @@ func (p TypeParameter) Equal(other *TypeParameter) bool { return p.Optional == other.Optional } -func (p TypeParameter) checkTypeBound(ty Type, typeRange ast.Range) error { +func (p TypeParameter) checkTypeBound(ty Type, memoryGauge common.MemoryGauge, typeRange ast.HasPosition) error { if p.TypeBound == nil || p.TypeBound.IsInvalidType() || ty.IsInvalidType() { @@ -3144,7 +3179,7 @@ func (p TypeParameter) checkTypeBound(ty Type, typeRange ast.Range) error { return &TypeMismatchError{ ExpectedType: p.TypeBound, ActualType: ty, - Range: typeRange, + Range: ast.NewRangeFromPositioned(memoryGauge, typeRange), } } @@ -3654,7 +3689,8 @@ func (t *FunctionType) Unify( other Type, typeParameters *TypeParameterTypeOrderedMap, report func(err error), - outerRange ast.Range, + memoryGauge common.MemoryGauge, + outerRange ast.HasPosition, ) ( result bool, ) { @@ -3684,6 +3720,7 @@ func (t *FunctionType) Unify( otherParameter.TypeAnnotation.Type, typeParameters, report, + memoryGauge, outerRange, ) result = result || parameterUnified @@ -3695,6 +3732,7 @@ func (t *FunctionType) Unify( otherFunction.ReturnTypeAnnotation.Type, typeParameters, report, + memoryGauge, outerRange, ) @@ -4798,7 +4836,13 @@ func (t *CompositeType) RewriteWithIntersectionTypes() (result Type, rewritten b return t, false } -func (*CompositeType) Unify(_ Type, _ *TypeParameterTypeOrderedMap, _ func(err error), _ ast.Range) bool { +func (*CompositeType) Unify( + _ Type, + _ *TypeParameterTypeOrderedMap, + _ func(err error), + _ common.MemoryGauge, + _ ast.HasPosition, +) bool { // TODO: return false } @@ -5578,7 +5622,13 @@ func (t *InterfaceType) RewriteWithIntersectionTypes() (Type, bool) { } -func (*InterfaceType) Unify(_ Type, _ *TypeParameterTypeOrderedMap, _ func(err error), _ ast.Range) bool { +func (*InterfaceType) Unify( + _ Type, + _ *TypeParameterTypeOrderedMap, + _ func(err error), + _ common.MemoryGauge, + _ ast.HasPosition, +) bool { // TODO: return false } @@ -6112,7 +6162,8 @@ func (t *DictionaryType) Unify( other Type, typeParameters *TypeParameterTypeOrderedMap, report func(err error), - outerRange ast.Range, + memoryGauge common.MemoryGauge, + outerRange ast.HasPosition, ) bool { otherDictionary, ok := other.(*DictionaryType) @@ -6120,8 +6171,22 @@ func (t *DictionaryType) Unify( return false } - keyUnified := t.KeyType.Unify(otherDictionary.KeyType, typeParameters, report, outerRange) - valueUnified := t.ValueType.Unify(otherDictionary.ValueType, typeParameters, report, outerRange) + keyUnified := t.KeyType.Unify( + otherDictionary.KeyType, + typeParameters, + report, + memoryGauge, + outerRange, + ) + + valueUnified := t.ValueType.Unify( + otherDictionary.ValueType, + typeParameters, + report, + memoryGauge, + outerRange, + ) + return keyUnified || valueUnified } @@ -6440,14 +6505,21 @@ func (t *InclusiveRangeType) Unify( other Type, typeParameters *TypeParameterTypeOrderedMap, report func(err error), - outerRange ast.Range, + memoryGauge common.MemoryGauge, + outerRange ast.HasPosition, ) bool { otherRange, ok := other.(*InclusiveRangeType) if !ok { return false } - return t.MemberType.Unify(otherRange.MemberType, typeParameters, report, outerRange) + return t.MemberType.Unify( + otherRange.MemberType, + typeParameters, + report, + memoryGauge, + outerRange, + ) } func (t *InclusiveRangeType) Resolve(typeArguments *TypeParameterTypeOrderedMap) Type { @@ -6726,14 +6798,21 @@ func (t *ReferenceType) Unify( other Type, typeParameters *TypeParameterTypeOrderedMap, report func(err error), - outerRange ast.Range, + memoryGauge common.MemoryGauge, + outerRange ast.HasPosition, ) bool { otherReference, ok := other.(*ReferenceType) if !ok { return false } - return t.Type.Unify(otherReference.Type, typeParameters, report, outerRange) + return t.Type.Unify( + otherReference.Type, + typeParameters, + report, + memoryGauge, + outerRange, + ) } func (t *ReferenceType) Resolve(typeArguments *TypeParameterTypeOrderedMap) Type { @@ -6848,7 +6927,13 @@ func (*AddressType) IsSuperType() bool { return false } -func (*AddressType) Unify(_ Type, _ *TypeParameterTypeOrderedMap, _ func(err error), _ ast.Range) bool { +func (*AddressType) Unify( + _ Type, + _ *TypeParameterTypeOrderedMap, + _ func(err error), + _ common.MemoryGauge, + _ ast.HasPosition, +) bool { return false } @@ -7480,7 +7565,13 @@ func (t *TransactionType) initializeMemberResolvers() { }) } -func (*TransactionType) Unify(_ Type, _ *TypeParameterTypeOrderedMap, _ func(err error), _ ast.Range) bool { +func (*TransactionType) Unify( + _ Type, + _ *TypeParameterTypeOrderedMap, + _ func(err error), + _ common.MemoryGauge, + _ ast.HasPosition, +) bool { return false } @@ -7762,7 +7853,13 @@ func (t *IntersectionType) SupportedEntitlements() (set *EntitlementOrderedSet) return set } -func (*IntersectionType) Unify(_ Type, _ *TypeParameterTypeOrderedMap, _ func(err error), _ ast.Range) bool { +func (*IntersectionType) Unify( + _ Type, + _ *TypeParameterTypeOrderedMap, + _ func(err error), + _ common.MemoryGauge, + _ ast.HasPosition, +) bool { // TODO: how do we unify the intersection sets? return false } @@ -7953,7 +8050,8 @@ func (t *CapabilityType) Unify( other Type, typeParameters *TypeParameterTypeOrderedMap, report func(err error), - outerRange ast.Range, + memoryGauge common.MemoryGauge, + outerRange ast.HasPosition, ) bool { otherCap, ok := other.(*CapabilityType) if !ok { @@ -7964,7 +8062,13 @@ func (t *CapabilityType) Unify( return false } - return t.BorrowType.Unify(otherCap.BorrowType, typeParameters, report, outerRange) + return t.BorrowType.Unify( + otherCap.BorrowType, + typeParameters, + report, + memoryGauge, + outerRange, + ) } func (t *CapabilityType) Resolve(typeArguments *TypeParameterTypeOrderedMap) Type { @@ -8513,7 +8617,13 @@ func (t *EntitlementType) RewriteWithIntersectionTypes() (Type, bool) { return t, false } -func (*EntitlementType) Unify(_ Type, _ *TypeParameterTypeOrderedMap, _ func(err error), _ ast.Range) bool { +func (*EntitlementType) Unify( + _ Type, + _ *TypeParameterTypeOrderedMap, + _ func(err error), + _ common.MemoryGauge, + _ ast.HasPosition, +) bool { return false } @@ -8664,7 +8774,13 @@ func (t *EntitlementMapType) RewriteWithIntersectionTypes() (Type, bool) { return t, false } -func (*EntitlementMapType) Unify(_ Type, _ *TypeParameterTypeOrderedMap, _ func(err error), _ ast.Range) bool { +func (*EntitlementMapType) Unify( + _ Type, + _ *TypeParameterTypeOrderedMap, + _ func(err error), + _ common.MemoryGauge, + _ ast.HasPosition, +) bool { return false } diff --git a/runtime/stdlib/test_test.go b/runtime/stdlib/test_test.go index c390d0616d..ae6a81585b 100644 --- a/runtime/stdlib/test_test.go +++ b/runtime/stdlib/test_test.go @@ -334,9 +334,9 @@ func TestTestNewMatcher(t *testing.T) { _, err := newTestContractInterpreter(t, script) - errs := checker.RequireCheckerErrors(t, err, 2) - assert.IsType(t, &sema.TypeParameterTypeMismatchError{}, errs[0]) - assert.IsType(t, &sema.TypeMismatchError{}, errs[1]) + errs := checker.RequireCheckerErrors(t, err, 1) + + assert.IsType(t, &sema.TypeMismatchError{}, errs[0]) }) t.Run("combined matcher mismatching types", func(t *testing.T) { @@ -499,9 +499,9 @@ func TestTestEqualMatcher(t *testing.T) { _, err := newTestContractInterpreter(t, script) - errs := checker.RequireCheckerErrors(t, err, 2) - assert.IsType(t, &sema.TypeParameterTypeMismatchError{}, errs[0]) - assert.IsType(t, &sema.TypeMismatchError{}, errs[1]) + errs := checker.RequireCheckerErrors(t, err, 1) + + assert.IsType(t, &sema.TypeMismatchError{}, errs[0]) }) t.Run("matcher or", func(t *testing.T) { @@ -1904,9 +1904,9 @@ func TestTestExpect(t *testing.T) { _, err := newTestContractInterpreter(t, script) - errs := checker.RequireCheckerErrors(t, err, 2) - assert.IsType(t, &sema.TypeParameterTypeMismatchError{}, errs[0]) - assert.IsType(t, &sema.TypeMismatchError{}, errs[1]) + errs := checker.RequireCheckerErrors(t, err, 1) + + assert.IsType(t, &sema.TypeMismatchError{}, errs[0]) }) t.Run("resource with resource matcher", func(t *testing.T) { diff --git a/runtime/tests/checker/account_test.go b/runtime/tests/checker/account_test.go index f0cf63e6bf..046bc5f6f8 100644 --- a/runtime/tests/checker/account_test.go +++ b/runtime/tests/checker/account_test.go @@ -219,16 +219,14 @@ func TestCheckAccountStorageSave(t *testing.T) { if domain == common.PathDomainStorage { - errs := RequireCheckerErrors(t, err, 2) + errs := RequireCheckerErrors(t, err, 1) - require.IsType(t, &sema.TypeParameterTypeMismatchError{}, errs[0]) - require.IsType(t, &sema.TypeMismatchError{}, errs[1]) + require.IsType(t, &sema.TypeMismatchError{}, errs[0]) } else { - errs := RequireCheckerErrors(t, err, 3) + errs := RequireCheckerErrors(t, err, 2) - require.IsType(t, &sema.TypeParameterTypeMismatchError{}, errs[0]) + require.IsType(t, &sema.TypeMismatchError{}, errs[0]) require.IsType(t, &sema.TypeMismatchError{}, errs[1]) - require.IsType(t, &sema.TypeMismatchError{}, errs[2]) } }) @@ -254,16 +252,14 @@ func TestCheckAccountStorageSave(t *testing.T) { if domain == common.PathDomainStorage { - errs := RequireCheckerErrors(t, err, 2) + errs := RequireCheckerErrors(t, err, 1) - require.IsType(t, &sema.TypeParameterTypeMismatchError{}, errs[0]) - require.IsType(t, &sema.TypeMismatchError{}, errs[1]) + require.IsType(t, &sema.TypeMismatchError{}, errs[0]) } else { - errs := RequireCheckerErrors(t, err, 3) + errs := RequireCheckerErrors(t, err, 2) - require.IsType(t, &sema.TypeParameterTypeMismatchError{}, errs[0]) + require.IsType(t, &sema.TypeMismatchError{}, errs[0]) require.IsType(t, &sema.TypeMismatchError{}, errs[1]) - require.IsType(t, &sema.TypeMismatchError{}, errs[2]) } }) } diff --git a/runtime/tests/checker/builtinfunctions_test.go b/runtime/tests/checker/builtinfunctions_test.go index 1bff1ba334..eede313982 100644 --- a/runtime/tests/checker/builtinfunctions_test.go +++ b/runtime/tests/checker/builtinfunctions_test.go @@ -292,7 +292,7 @@ func TestCheckRevertibleRandom(t *testing.T) { } } - runCase := func(t *testing.T, ty sema.Type, code string) { + runValidCase := func(t *testing.T, ty sema.Type, code string) { checker, err := ParseAndCheckWithOptions(t, code, newOptions()) @@ -307,7 +307,7 @@ func TestCheckRevertibleRandom(t *testing.T) { t.Parallel() code := fmt.Sprintf("let rand = revertibleRandom<%s>()", ty) - runCase(t, ty, code) + runValidCase(t, ty, code) }) } @@ -316,7 +316,7 @@ func TestCheckRevertibleRandom(t *testing.T) { t.Parallel() code := fmt.Sprintf("let rand = revertibleRandom<%[1]s>(modulo: %[1]s(1))", ty) - runCase(t, ty, code) + runValidCase(t, ty, code) }) } @@ -394,7 +394,6 @@ func TestCheckRevertibleRandom(t *testing.T) { "modulo type mismatch", "let rand = revertibleRandom(modulo: UInt128(1))", []error{ - &sema.TypeParameterTypeMismatchError{}, &sema.TypeMismatchError{}, }, ) @@ -404,18 +403,17 @@ func TestCheckRevertibleRandom(t *testing.T) { "string modulo", `let rand = revertibleRandom(modulo: "abcd")`, []error{ - &sema.TypeParameterTypeMismatchError{}, &sema.TypeMismatchError{}, }, ) - // This is an error since we do not support type inference of function arguments. - runInvalidCase( - t, - "missing type inference", - "let rand = revertibleRandom(modulo: 1)", - []error{ - &sema.TypeParameterTypeMismatchError{}, - }, - ) + t.Run("type parameter used for argument", func(t *testing.T) { + t.Parallel() + + runValidCase( + t, + sema.UInt256Type, + "let rand = revertibleRandom(modulo: 1)", + ) + }) } diff --git a/runtime/tests/checker/genericfunction_test.go b/runtime/tests/checker/genericfunction_test.go index 00ccd2fc5a..22559c9acf 100644 --- a/runtime/tests/checker/genericfunction_test.go +++ b/runtime/tests/checker/genericfunction_test.go @@ -283,10 +283,9 @@ func TestCheckGenericFunctionInvocation(t *testing.T) { }, ) - errs := RequireCheckerErrors(t, err, 2) + errs := RequireCheckerErrors(t, err, 1) - assert.IsType(t, &sema.TypeParameterTypeMismatchError{}, errs[0]) - assert.IsType(t, &sema.TypeMismatchError{}, errs[1]) + assert.IsType(t, &sema.TypeMismatchError{}, errs[0]) }) t.Run("valid: one type parameter, one type argument, one parameter, one arguments", func(t *testing.T) { @@ -423,10 +422,9 @@ func TestCheckGenericFunctionInvocation(t *testing.T) { }, ) - errs := RequireCheckerErrors(t, err, 2) + errs := RequireCheckerErrors(t, err, 1) - assert.IsType(t, &sema.TypeParameterTypeMismatchError{}, errs[0]) - assert.IsType(t, &sema.TypeMismatchError{}, errs[1]) + assert.IsType(t, &sema.TypeMismatchError{}, errs[0]) }) t.Run("invalid: one type parameter, no type argument, no parameters, no arguments, return type", func(t *testing.T) { diff --git a/runtime/tests/checker/range_value_test.go b/runtime/tests/checker/range_value_test.go index 5bc94600b3..4e64dd56fc 100644 --- a/runtime/tests/checker/range_value_test.go +++ b/runtime/tests/checker/range_value_test.go @@ -330,13 +330,17 @@ func TestCheckInclusiveRangeConstructionInvalid(t *testing.T) { t, typeString, fmt.Sprintf("let r = InclusiveRange(%s(1), %s(2))", typeString, differentTypeString), - []error{&sema.TypeParameterTypeMismatchError{}, &sema.TypeMismatchError{}}, + []error{ + &sema.TypeMismatchError{}, + }, ) runInvalidCase( t, typeString, fmt.Sprintf("let r = InclusiveRange(%s(1), %s(10), step: %s(2))", typeString, typeString, differentTypeString), - []error{&sema.TypeParameterTypeMismatchError{}, &sema.TypeMismatchError{}}, + []error{ + &sema.TypeMismatchError{}, + }, ) // Not enough arguments diff --git a/runtime/tests/checker/type_inference_test.go b/runtime/tests/checker/type_inference_test.go index 6347f8083d..d8b2298cb7 100644 --- a/runtime/tests/checker/type_inference_test.go +++ b/runtime/tests/checker/type_inference_test.go @@ -335,7 +335,7 @@ func TestCheckFunctionArgumentTypeInference(t *testing.T) { require.NoError(t, err) }) - t.Run("with generics", func(t *testing.T) { + t.Run("with generics, void return type", func(t *testing.T) { t.Parallel() @@ -367,46 +367,146 @@ func TestCheckFunctionArgumentTypeInference(t *testing.T) { }, ) - errs := RequireCheckerErrors(t, err, 2) + require.NoError(t, err) + }) - require.IsType(t, &sema.TypeParameterTypeMismatchError{}, errs[0]) - typeParamMismatchErr := errs[0].(*sema.TypeParameterTypeMismatchError) - assert.Equal( - t, - &sema.VariableSizedType{ - Type: sema.Int8Type, - }, - typeParamMismatchErr.ExpectedType, - ) + t.Run("with generics, generic return type", func(t *testing.T) { - assert.Equal( - t, - &sema.VariableSizedType{ - Type: sema.IntType, + t.Parallel() + + typeParameter := &sema.TypeParameter{ + Name: "T", + TypeBound: nil, + } + + _, err := parseAndCheckWithTestValue(t, + ` + let res: [Int8] = test<[Int8]>([1, 2, 3]) + `, + &sema.FunctionType{ + TypeParameters: []*sema.TypeParameter{ + typeParameter, + }, + Parameters: []sema.Parameter{ + { + Label: sema.ArgumentLabelNotRequired, + Identifier: "value", + TypeAnnotation: sema.NewTypeAnnotation( + &sema.GenericType{ + TypeParameter: typeParameter, + }, + ), + }, + }, + ReturnTypeAnnotation: sema.NewTypeAnnotation( + &sema.GenericType{ + TypeParameter: typeParameter, + }, + ), }, - typeParamMismatchErr.ActualType, ) - require.IsType(t, &sema.TypeMismatchError{}, errs[1]) - typeMismatchErr := errs[1].(*sema.TypeMismatchError) + require.NoError(t, err) + }) + + t.Run("with generics, argument type propagation, simple", func(t *testing.T) { - assert.Equal( - t, - &sema.VariableSizedType{ - Type: sema.Int8Type, + t.Parallel() + + typeParameter := &sema.TypeParameter{ + Name: "T", + TypeBound: nil, + } + + _, err := parseAndCheckWithTestValue(t, + ` + let res: UInt8 = test(1 as UInt8, 2) + `, + &sema.FunctionType{ + TypeParameters: []*sema.TypeParameter{ + typeParameter, + }, + Parameters: []sema.Parameter{ + { + Label: sema.ArgumentLabelNotRequired, + Identifier: "a", + TypeAnnotation: sema.NewTypeAnnotation( + &sema.GenericType{ + TypeParameter: typeParameter, + }, + ), + }, + { + Label: sema.ArgumentLabelNotRequired, + Identifier: "b", + TypeAnnotation: sema.NewTypeAnnotation( + &sema.GenericType{ + TypeParameter: typeParameter, + }, + ), + }, + }, + ReturnTypeAnnotation: sema.NewTypeAnnotation( + &sema.GenericType{ + TypeParameter: typeParameter, + }, + ), }, - typeMismatchErr.ExpectedType, ) - assert.Equal( - t, - &sema.VariableSizedType{ - Type: sema.IntType, + require.NoError(t, err) + }) + + t.Run("with generics, argument type propagation, nested", func(t *testing.T) { + + t.Parallel() + + typeParameter := &sema.TypeParameter{ + Name: "T", + TypeBound: nil, + } + + _, err := parseAndCheckWithTestValue(t, + ` + let res: UInt8 = test(1 as UInt8, [2]) + `, + &sema.FunctionType{ + TypeParameters: []*sema.TypeParameter{ + typeParameter, + }, + Parameters: []sema.Parameter{ + { + Label: sema.ArgumentLabelNotRequired, + Identifier: "a", + TypeAnnotation: sema.NewTypeAnnotation( + &sema.GenericType{ + TypeParameter: typeParameter, + }, + ), + }, + { + Label: sema.ArgumentLabelNotRequired, + Identifier: "b", + TypeAnnotation: sema.NewTypeAnnotation( + &sema.VariableSizedType{ + Type: &sema.GenericType{ + TypeParameter: typeParameter, + }, + }, + ), + }, + }, + ReturnTypeAnnotation: sema.NewTypeAnnotation( + &sema.GenericType{ + TypeParameter: typeParameter, + }, + ), }, - typeMismatchErr.ActualType, ) + require.NoError(t, err) }) + } func TestCheckBinaryExpressionTypeInference(t *testing.T) {