Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 31 additions & 1 deletion Coder.Test/Languages/GoGeneratedSourceCompilesTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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})
}

Expand Down Expand Up @@ -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
{
Expand All @@ -170,6 +171,35 @@ private static SourceFile Exemplar()
return file;
}

/// <summary>
/// Builds a value constructed from a type and two plain arguments, which is the shape neither of
/// the other two composite literals here has.
/// </summary>
/// <returns>The declaration.</returns>
/// <remarks>
/// This is here because it is the one construction Go cannot write as a call: a conversion takes
/// one operand, so <c>Point(1, 2)</c> is <c>too many arguments in conversion to Point</c> 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.
/// </remarks>
private static FieldDeclaration Corner()
{
ConstructionExpression built = new(new TypeReference("Point"));
built.Arguments.Add(new LiteralExpression<int>(1));
built.Arguments.Add(new LiteralExpression<int>(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;
}

/// <summary>
/// Builds the destructor, which becomes the <c>Close</c> the caller has to call.
/// </summary>
Expand Down
25 changes: 25 additions & 0 deletions Coder.Test/Languages/GoGeneratorTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -641,6 +641,31 @@ public void BareListInitialiser_TakesTheDeclarationsType()
Assert.AreEqual($"var Sizes []int = []int{{1, 2}}{NewLine}", Generator.Generate(sizes));
}

/// <summary>
/// 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.
/// </summary>
[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"))));
}

/// <summary>
/// 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.
Expand Down
27 changes: 12 additions & 15 deletions Coder/Languages/GoGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1583,9 +1583,16 @@ private static string BranchType(ConditionalExpression conditional) =>
/// <remarks>
/// 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 <c>int64(n)</c> 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
/// <c>int64(n)</c> is.
/// <para>
/// The argument count is what parts the last two, and it has to be: a conversion takes one
/// operand and nothing else, so <c>Wrapper(1, 2)</c> is not a longer conversion but a compile
/// error — <c>too many arguments in conversion to Wrapper</c>. 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.
/// </para>
/// </remarks>
protected override void GenerateConstructionExpression(ConstructionExpression construction, CodeBlocker code)
{
Expand All @@ -1600,25 +1607,15 @@ 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, "{", "}", "{}");
return;
}

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(")");
}

Expand Down