diff --git a/Coder.Test/Languages/GoGeneratedSourceCompilesTests.cs b/Coder.Test/Languages/GoGeneratedSourceCompilesTests.cs
index ae8e517..1833d9f 100644
--- a/Coder.Test/Languages/GoGeneratedSourceCompilesTests.cs
+++ b/Coder.Test/Languages/GoGeneratedSourceCompilesTests.cs
@@ -83,7 +83,7 @@ func use() int {
// circle.Sum is Point's, reached through the embedded field rather than inherited.
return built.Sum() + zeroed.y + first.x + named.x + circle.Sum() + built.Pick() +
- built.Add(zeroed).x + built.Negate().x +
+ built.Add(zeroed).x + built.Negate().x + Corner.y +
int(built.ToFloat64()) + int(shape.area()) + int(ColourGreen) + measure("a", []int{1})
}
@@ -160,6 +160,7 @@ private static SourceFile Exemplar()
file.Members.Add(DescribesPoint());
file.Members.Add(CompiledExemplar.OriginAlias());
file.Members.Add(CompiledExemplar.OriginTable());
+ file.Members.Add(Corner());
file.Members.Add(CompiledExemplar.Measure());
file.Members.Add(new CompileTimeAssertion
{
@@ -170,6 +171,35 @@ private static SourceFile Exemplar()
return file;
}
+ ///
+ /// Builds a value constructed from a type and two plain arguments, which is the shape neither of
+ /// the other two composite literals here has.
+ ///
+ /// The declaration.
+ ///
+ /// This is here because it is the one construction Go cannot write as a call: a conversion takes
+ /// one operand, so Point(1, 2) is too many arguments in conversion to Point rather
+ /// than a longer conversion, and only a compiler says so. The table above lists its members by
+ /// name and the assertion below constructs nothing at all, so without this the positional
+ /// literal is the one shape nothing compiles.
+ ///
+ private static FieldDeclaration Corner()
+ {
+ ConstructionExpression built = new(new TypeReference("Point"));
+ built.Arguments.Add(new LiteralExpression(1));
+ built.Arguments.Add(new LiteralExpression(2));
+
+ FieldDeclaration corner = new()
+ {
+ Name = "Corner",
+ Type = new TypeReference("Point"),
+ InitialValue = built,
+ };
+ corner.Documentation.Add("The far corner, built from its members in order.");
+
+ return corner;
+ }
+
///
/// Builds the destructor, which becomes the Close the caller has to call.
///
diff --git a/Coder.Test/Languages/GoGeneratorTests.cs b/Coder.Test/Languages/GoGeneratorTests.cs
index 03a014c..b3a267b 100644
--- a/Coder.Test/Languages/GoGeneratorTests.cs
+++ b/Coder.Test/Languages/GoGeneratorTests.cs
@@ -641,6 +641,31 @@ public void BareListInitialiser_TakesTheDeclarationsType()
Assert.AreEqual($"var Sizes []int = []int{{1, 2}}{NewLine}", Generator.Generate(sizes));
}
+ ///
+ /// Tests that a construction is written as a conversion only where Go has one — a type and
+ /// exactly one operand — and as a composite literal everywhere else. A conversion taking two
+ /// arguments is not a longer conversion but a compile error, so the argument count is what parts
+ /// the two shapes rather than whether any argument names a member.
+ ///
+ [TestMethod]
+ public void Construction_IsAConversionOnlyWhereItTakesOneValue()
+ {
+ ConstructionExpression named = new(new TypeReference("Point"));
+ named.Arguments.Add(new MemberInitialiser("X") { Value = Literal.Number(1) });
+
+ ConstructionExpression converted = new(new TypeReference("int64"));
+ converted.Arguments.Add(new VariableReference("n"));
+
+ ConstructionExpression positional = new(new TypeReference("Wrapper"));
+ positional.Arguments.Add(Literal.Number(1));
+ positional.Arguments.Add(Literal.Number(2));
+
+ Assert.AreEqual("Point{X: 1}", Generator.Generate(named));
+ Assert.AreEqual("int64(n)", Generator.Generate(converted));
+ Assert.AreEqual("Wrapper{1, 2}", Generator.Generate(positional));
+ Assert.AreEqual("Point{}", Generator.Generate(new ConstructionExpression(new TypeReference("Point"))));
+ }
+
///
/// Tests that a list whose elements are themselves lists is written one per line, so that adding
/// a row to a generated table touches one line.
diff --git a/Coder/Languages/GoGenerator.cs b/Coder/Languages/GoGenerator.cs
index ee5034c..8318dba 100644
--- a/Coder/Languages/GoGenerator.cs
+++ b/Coder/Languages/GoGenerator.cs
@@ -1583,9 +1583,16 @@ private static string BranchType(ConditionalExpression conditional) =>
///
/// Three shapes, and which one is written depends on what the expression is rather than on where
/// it stands: a construction naming its members is a composite literal, one with no type at all
- /// is a composite literal whose type the declaration around it supplies, and one that names
- /// neither is a call — which in Go is a conversion when it takes one argument, since that is
- /// what int64(n) is.
+ /// is a composite literal whose type the declaration around it supplies, and one naming a type
+ /// and handing it exactly one value is a call — which in Go is a conversion, since that is what
+ /// int64(n) is.
+ ///
+ /// The argument count is what parts the last two, and it has to be: a conversion takes one
+ /// operand and nothing else, so Wrapper(1, 2) is not a longer conversion but a compile
+ /// error — too many arguments in conversion to Wrapper. A type handed two values
+ /// positionally is the other kind of composite literal, the one that lists a struct's fields in
+ /// declaration order, so anything but one argument is written in braces.
+ ///
///
protected override void GenerateConstructionExpression(ConstructionExpression construction, CodeBlocker code)
{
@@ -1600,7 +1607,7 @@ protected override void GenerateConstructionExpression(ConstructionExpression co
string type = SpellType(construction.Type);
- if (construction.Arguments.Count == 0 || construction.Arguments.Any(argument => argument is MemberInitialiser))
+ if (construction.Arguments.Count != 1 || construction.Arguments.Any(argument => argument is MemberInitialiser))
{
code.Write(type);
WriteElementList(construction, code, "{", "}", "{}");
@@ -1608,17 +1615,7 @@ protected override void GenerateConstructionExpression(ConstructionExpression co
}
code.Write($"{type}(");
-
- for (int index = 0; index < construction.Arguments.Count; index++)
- {
- if (index > 0)
- {
- code.Write(", ");
- }
-
- GenerateInternal(construction.Arguments[index], code);
- }
-
+ GenerateInternal(construction.Arguments[0], code);
code.Write(")");
}