diff --git a/TestModels/Positional/Makefile b/TestModels/Positional/Makefile index b39ff1434..4caab2bbb 100644 --- a/TestModels/Positional/Makefile +++ b/TestModels/Positional/Makefile @@ -46,3 +46,8 @@ INDEX_FILE_WITHOUT_EXTERN_STRING="module SimplePositional refines AbstractSimple WRAPPED_INDEX_FILE_PATH=src/WrappedSimplePositionalImpl.dfy WRAPPED_INDEX_FILE_WITH_EXTERN_STRING="module {:options \"--function-syntax:4\"} {:extern \"simple.positional.internaldafny.wrapped\"} WrappedSimplePositionalService refines WrappedAbstractSimplePositionalService {" WRAPPED_INDEX_FILE_WITHOUT_EXTERN_STRING="module {:options \"--function-syntax:4\"} WrappedSimplePositionalService refines WrappedAbstractSimplePositionalService {" + +GO_MODULE_NAME="github.com/smithy-lang/smithy-dafny/TestModels/Positional" + +TRANSLATION_RECORD_GO := \ + dafny-dependencies/StandardLibrary/runtimes/go/ImplementationFromDafny-go/ImplementationFromDafny-go.dtr \ No newline at end of file diff --git a/TestModels/Positional/runtimes/go/ImplementationFromDafny-go/go.mod b/TestModels/Positional/runtimes/go/ImplementationFromDafny-go/go.mod new file mode 100644 index 000000000..075e660ad --- /dev/null +++ b/TestModels/Positional/runtimes/go/ImplementationFromDafny-go/go.mod @@ -0,0 +1,10 @@ +module github.com/smithy-lang/smithy-dafny/TestModels/Positional + +go 1.23.0 + +require ( + github.com/dafny-lang/DafnyRuntimeGo/v4 v4.8.0 + github.com/dafny-lang/DafnyStandardLibGo v0.0.0 +) + +replace github.com/dafny-lang/DafnyStandardLibGo => ../../../../dafny-dependencies/StandardLibrary/runtimes/go/ImplementationFromDafny-go/ diff --git a/TestModels/Positional/runtimes/go/TestsFromDafny-go/go.mod b/TestModels/Positional/runtimes/go/TestsFromDafny-go/go.mod new file mode 100644 index 000000000..27a1a10bb --- /dev/null +++ b/TestModels/Positional/runtimes/go/TestsFromDafny-go/go.mod @@ -0,0 +1,14 @@ +module github.com/smithy-lang/smithy-dafny/TestModels/Positional/test + +go 1.23.0 + +require github.com/dafny-lang/DafnyStandardLibGo v0.0.0 + +require ( + github.com/dafny-lang/DafnyRuntimeGo/v4 v4.8.0 + github.com/smithy-lang/smithy-dafny/TestModels/Positional v0.0.0 +) + +replace github.com/smithy-lang/smithy-dafny/TestModels/Positional v0.0.0 => ../ImplementationFromDafny-go + +replace github.com/dafny-lang/DafnyStandardLibGo => ../../../../dafny-dependencies/StandardLibrary/runtimes/go/ImplementationFromDafny-go/ diff --git a/codegen/smithy-dafny-codegen-test/src/test/java/software/amazon/polymorph/smithygo/GoTestModels.java b/codegen/smithy-dafny-codegen-test/src/test/java/software/amazon/polymorph/smithygo/GoTestModels.java index 0081a479f..8293b8b4b 100644 --- a/codegen/smithy-dafny-codegen-test/src/test/java/software/amazon/polymorph/smithygo/GoTestModels.java +++ b/codegen/smithy-dafny-codegen-test/src/test/java/software/amazon/polymorph/smithygo/GoTestModels.java @@ -35,9 +35,6 @@ class GoTestModels extends TestModelTest { DISABLED_TESTS.add("MultipleModels"); DISABLED_TESTS.add("LocalService"); - //TODO: Pending PR Merge - DISABLED_TESTS.add("Positional"); - //V1 Tests are not supported in Go DISABLED_TESTS.add("aws-sdks/ddb"); DISABLED_TESTS.add("aws-sdks/kms"); diff --git a/codegen/smithy-dafny-codegen/src/main/java/software/amazon/polymorph/smithygo/localservice/DafnyLocalServiceGenerator.java b/codegen/smithy-dafny-codegen/src/main/java/software/amazon/polymorph/smithygo/localservice/DafnyLocalServiceGenerator.java index d07648227..5ea11deb9 100644 --- a/codegen/smithy-dafny-codegen/src/main/java/software/amazon/polymorph/smithygo/localservice/DafnyLocalServiceGenerator.java +++ b/codegen/smithy-dafny-codegen/src/main/java/software/amazon/polymorph/smithygo/localservice/DafnyLocalServiceGenerator.java @@ -17,8 +17,10 @@ import software.amazon.polymorph.smithygo.localservice.nameresolver.SmithyNameResolver; import software.amazon.polymorph.traits.ExtendableTrait; import software.amazon.polymorph.traits.LocalServiceTrait; +import software.amazon.polymorph.traits.PositionalTrait; import software.amazon.polymorph.traits.ReferenceTrait; import software.amazon.smithy.aws.traits.ServiceTrait; +import software.amazon.smithy.codegen.core.Symbol; import software.amazon.smithy.codegen.core.SymbolProvider; import software.amazon.smithy.model.Model; import software.amazon.smithy.model.knowledge.TopDownIndex; @@ -212,7 +214,7 @@ func NewClient(clientConfig $L) (*$T, error) { SmithyNameResolver.smithyTypesNamespace(inputShape), inputShape.toShapeId().getName() ); - final var outputType = outputShape.hasTrait(UnitTypeTrait.class) + var outputType = outputShape.hasTrait(UnitTypeTrait.class) ? "" : "*%s.%s,".formatted( SmithyNameResolver.smithyTypesNamespace(outputShape), @@ -241,15 +243,38 @@ func NewClient(clientConfig $L) (*$T, error) { operationShape.getId().getName() ); } else { + String dafnyType; + if (inputShape.hasTrait(PositionalTrait.class)) { + Shape inputForPositional = model.expectShape( + inputShape + .getAllMembers() + .values() + .stream() + .findFirst() + .get() + .getTarget() + ); + Symbol symbolForPositional = symbolProvider.toSymbol( + inputForPositional + ); + dafnyType = + DafnyNameResolver.getDafnyType( + inputForPositional, + symbolForPositional + ); + } else { + dafnyType = + DafnyNameResolver.getDafnyType( + inputShape, + symbolProvider.toSymbol(inputShape) + ); + } baseClientCall = """ var dafny_request %s = %s(params) var dafny_response = client.DafnyClient.%s(dafny_request) """.formatted( - DafnyNameResolver.getDafnyType( - inputShape, - symbolProvider.toSymbol(inputShape) - ), + dafnyType, SmithyNameResolver.getToDafnyMethodName( service, inputShape, @@ -264,21 +289,30 @@ func NewClient(clientConfig $L) (*$T, error) { returnResponse = "return nil"; returnError = "return"; } else { - returnResponse = - """ - var native_response = %s(dafny_response.Extract().(%s)) - return &native_response, nil - """.formatted( - SmithyNameResolver.getFromDafnyMethodName( - service, - outputShape, - "" - ), - DafnyNameResolver.getDafnyType( - outputShape, - symbolProvider.toSymbol(outputShape) - ) - ); + if (outputShape.hasTrait(PositionalTrait.class)) { + outputType = "interface{},"; + returnResponse = + """ + var native_response = dafny_response.Extract() + return native_response, nil + """; + } else { + returnResponse = + """ + var native_response = %s(dafny_response.Extract().(%s)) + return &native_response, nil + """.formatted( + SmithyNameResolver.getFromDafnyMethodName( + service, + outputShape, + "" + ), + DafnyNameResolver.getDafnyType( + outputShape, + symbolProvider.toSymbol(outputShape) + ) + ); + } returnError = "return nil,"; } @@ -396,7 +430,8 @@ void generateShim() { final var outputShape = model.expectShape( operationShape.getOutputShape() ); - final var inputType = inputShape.hasTrait(UnitTypeTrait.class) + // this is maybe because positional trait can change this + final var maybeInputType = inputShape.hasTrait(UnitTypeTrait.class) ? "" : "input %s".formatted( DafnyNameResolver.getDafnyType( @@ -404,7 +439,7 @@ void generateShim() { symbolProvider.toSymbol(inputShape) ) ); - + final String inputType; final var typeConversion = inputShape.hasTrait(UnitTypeTrait.class) ? "" : "var native_request = %s(input)".formatted( @@ -434,6 +469,32 @@ void generateShim() { SmithyNameResolver.getToDafnyMethodName(outputShape, "") ); } + if (inputShape.hasTrait(PositionalTrait.class)) { + writer.addImportFromModule( + "github.com/dafny-lang/DafnyRuntimeGo", + "dafny" + ); + Shape inputForPositional = model.expectShape( + inputShape + .getAllMembers() + .values() + .stream() + .findFirst() + .get() + .getTarget() + ); + Symbol symbolForPositional = symbolProvider.toSymbol( + inputForPositional + ); + String dafnyType = DafnyNameResolver.getDafnyType( + inputForPositional, + symbolForPositional + ); + inputType = "input %s".formatted(dafnyType); + returnResponse = "(native_response)"; + } else { + inputType = maybeInputType; + } writer.write( """ diff --git a/codegen/smithy-dafny-codegen/src/main/java/software/amazon/polymorph/smithygo/localservice/DafnyLocalServiceTypeConversionProtocol.java b/codegen/smithy-dafny-codegen/src/main/java/software/amazon/polymorph/smithygo/localservice/DafnyLocalServiceTypeConversionProtocol.java index 67e213269..87cf96007 100644 --- a/codegen/smithy-dafny-codegen/src/main/java/software/amazon/polymorph/smithygo/localservice/DafnyLocalServiceTypeConversionProtocol.java +++ b/codegen/smithy-dafny-codegen/src/main/java/software/amazon/polymorph/smithygo/localservice/DafnyLocalServiceTypeConversionProtocol.java @@ -22,11 +22,14 @@ import software.amazon.polymorph.smithygo.utils.GoCodegenUtils; import software.amazon.polymorph.traits.ExtendableTrait; import software.amazon.polymorph.traits.LocalServiceTrait; +import software.amazon.polymorph.traits.PositionalTrait; import software.amazon.polymorph.traits.ReferenceTrait; import software.amazon.smithy.aws.traits.ServiceTrait; +import software.amazon.smithy.codegen.core.Symbol; import software.amazon.smithy.model.shapes.OperationShape; import software.amazon.smithy.model.shapes.ResourceShape; import software.amazon.smithy.model.shapes.ServiceShape; +import software.amazon.smithy.model.shapes.Shape; import software.amazon.smithy.model.shapes.ShapeId; import software.amazon.smithy.model.shapes.StructureShape; import software.amazon.smithy.model.traits.ErrorTrait; @@ -78,6 +81,29 @@ public void generateSerializers(final GenerationContext context) { final var inputToDafnyMethodName = SmithyNameResolver.getToDafnyMethodName(serviceShape, input, ""); final var inputSymbol = symbolProvider.toSymbol(input); + final String outputType; + if (input.hasTrait(PositionalTrait.class)) { + // Output type in To Dafny should be unwrapped + Shape inputForPositional = model.expectShape( + input + .getAllMembers() + .values() + .stream() + .findFirst() + .get() + .getTarget() + ); + Symbol symbolForPositional = symbolProvider.toSymbol( + inputForPositional + ); + outputType = + DafnyNameResolver.getDafnyType( + inputForPositional, + symbolForPositional + ); + } else { + outputType = DafnyNameResolver.getDafnyType(input, inputSymbol); + } writerDelegator.useFileWriter( "%s/%s".formatted( SmithyNameResolver.shapeNamespace(serviceShape), @@ -98,7 +124,7 @@ public void generateSerializers(final GenerationContext context) { }""", inputToDafnyMethodName, SmithyNameResolver.getSmithyType(input, inputSymbol), - DafnyNameResolver.getDafnyType(input, inputSymbol), + outputType, writer.consumer(w -> generateRequestSerializer( context, @@ -113,7 +139,10 @@ public void generateSerializers(final GenerationContext context) { } final var output = model.expectShape(operation.getOutputShape()); - if (!alreadyVisited.contains(output.toShapeId())) { + if ( + !alreadyVisited.contains(output.toShapeId()) && + !output.hasTrait(PositionalTrait.class) + ) { alreadyVisited.add(output.toShapeId()); if ( !output.hasTrait(UnitTypeTrait.class) && @@ -370,6 +399,31 @@ public void generateDeserializers(final GenerationContext context) { "" ); final var inputSymbol = context.symbolProvider().toSymbol(input); + final String inputType; + if (input.hasTrait(PositionalTrait.class)) { + // Input type in To native should be unwrapped + Shape inputForPositional = context + .model() + .expectShape( + input + .getAllMembers() + .values() + .stream() + .findFirst() + .get() + .getTarget() + ); + Symbol symbolForPositional = context + .symbolProvider() + .toSymbol(inputForPositional); + inputType = + DafnyNameResolver.getDafnyType( + inputForPositional, + symbolForPositional + ); + } else { + inputType = DafnyNameResolver.getDafnyType(input, inputSymbol); + } delegator.useFileWriter( "%s/%s".formatted( SmithyNameResolver.shapeNamespace(serviceShape), @@ -390,7 +444,7 @@ public void generateDeserializers(final GenerationContext context) { ${C|} }""", inputFromDafnyMethodName, - DafnyNameResolver.getDafnyType(input, inputSymbol), + inputType, SmithyNameResolver.getSmithyType(input, inputSymbol), writer.consumer(w -> generateRequestDeserializer( @@ -408,7 +462,10 @@ public void generateDeserializers(final GenerationContext context) { final var output = context .model() .expectShape(operation.getOutputShape()); - if (!alreadyVisited.contains(output.toShapeId())) { + if ( + !alreadyVisited.contains(output.toShapeId()) && + !output.hasTrait(PositionalTrait.class) + ) { alreadyVisited.add(output.toShapeId()); if ( !output.hasTrait(UnitTypeTrait.class) && diff --git a/codegen/smithy-dafny-codegen/src/main/java/software/amazon/polymorph/smithygo/localservice/shapevisitor/DafnyToSmithyShapeVisitor.java b/codegen/smithy-dafny-codegen/src/main/java/software/amazon/polymorph/smithygo/localservice/shapevisitor/DafnyToSmithyShapeVisitor.java index 36689bcd2..19bf96b88 100644 --- a/codegen/smithy-dafny-codegen/src/main/java/software/amazon/polymorph/smithygo/localservice/shapevisitor/DafnyToSmithyShapeVisitor.java +++ b/codegen/smithy-dafny-codegen/src/main/java/software/amazon/polymorph/smithygo/localservice/shapevisitor/DafnyToSmithyShapeVisitor.java @@ -13,6 +13,7 @@ import software.amazon.polymorph.smithygo.localservice.nameresolver.SmithyNameResolver; import software.amazon.polymorph.smithygo.utils.GoCodegenUtils; import software.amazon.polymorph.traits.DafnyUtf8BytesTrait; +import software.amazon.polymorph.traits.PositionalTrait; import software.amazon.polymorph.traits.ReferenceTrait; import software.amazon.smithy.aws.traits.ServiceTrait; import software.amazon.smithy.codegen.core.CodegenException; @@ -276,6 +277,13 @@ public String structureShape(final StructureShape shape) { final var targetShape = context .model() .expectShape(memberShape.getTarget()); + final String DtorConversion; + if (!shape.hasTrait(PositionalTrait.class)) { + DtorConversion = ".Dtor_%s()".formatted(memberName); + } else { + // Shapes with PositionalTrait already gets input unwrapped so no conversion needed. + DtorConversion = ""; + } //TODO: Is it ever possible for structure to be nil? String maybeAssertion = ""; if (dataSource.equals("input")) { @@ -296,7 +304,7 @@ public String structureShape(final StructureShape shape) { "%1$s%2$s%3$s%4$s%5$s".formatted( dataSource, maybeAssertion, - ".Dtor_%s()".formatted(memberName), + DtorConversion, memberShape.isOptional() ? ".UnwrapOr(nil)" : "", assertionRequired ? ".(%s)".formatted( diff --git a/codegen/smithy-dafny-codegen/src/main/java/software/amazon/polymorph/smithygo/localservice/shapevisitor/SmithyToDafnyShapeVisitor.java b/codegen/smithy-dafny-codegen/src/main/java/software/amazon/polymorph/smithygo/localservice/shapevisitor/SmithyToDafnyShapeVisitor.java index df9ef3c9f..5b76240fb 100644 --- a/codegen/smithy-dafny-codegen/src/main/java/software/amazon/polymorph/smithygo/localservice/shapevisitor/SmithyToDafnyShapeVisitor.java +++ b/codegen/smithy-dafny-codegen/src/main/java/software/amazon/polymorph/smithygo/localservice/shapevisitor/SmithyToDafnyShapeVisitor.java @@ -12,6 +12,7 @@ import software.amazon.polymorph.smithygo.localservice.nameresolver.DafnyNameResolver; import software.amazon.polymorph.smithygo.localservice.nameresolver.SmithyNameResolver; import software.amazon.polymorph.traits.DafnyUtf8BytesTrait; +import software.amazon.polymorph.traits.PositionalTrait; import software.amazon.polymorph.traits.ReferenceTrait; import software.amazon.smithy.aws.traits.ServiceTrait; import software.amazon.smithy.codegen.core.CodegenException; @@ -262,15 +263,26 @@ public String structureShape(final StructureShape shape) { nilCheck = "if %s == nil {return %s}".formatted(dataSource, nilWrapIfRequired); } - final var goCodeBlock = - """ - func () %s { - %s - return %s - }()"""; - - builder.append("%1$s(".formatted(companionStruct)); - final String fieldSeparator = ","; + final String goCodeBlock; + final String fieldSeparator; + if (!shape.hasTrait(PositionalTrait.class)) { + builder.append("%1$s(".formatted(companionStruct)); + goCodeBlock = + """ + func () %s { + %s + return %s + }()"""; + fieldSeparator = ","; + } else { + // Positional trait can only have one variable. + fieldSeparator = ""; + // Don't unwrap position trait shape + goCodeBlock = + """ + %s + """; + } for (final var memberShapeEntry : shape.getAllMembers().entrySet()) { final var memberName = memberShapeEntry.getKey(); final var memberShape = memberShapeEntry.getValue(); @@ -293,7 +305,9 @@ public String structureShape(final StructureShape shape) { ) ); } - + if (shape.hasTrait(PositionalTrait.class)) { + return goCodeBlock.formatted(builder.toString()); + } return goCodeBlock.formatted( returnType, nilCheck,