Skip to content

Commit 8eee9f7

Browse files
authored
Preserve constrained setters through spills (#2762)
1 parent eaf53a3 commit 8eee9f7

3 files changed

Lines changed: 93 additions & 2 deletions

File tree

src/Core/CodeAnalysis/Lowering/Async/SpillSequenceSpiller.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -500,7 +500,14 @@ private BoundSpillSequenceExpression SpillExpression(BoundExpression expression)
500500
clrPropAssign,
501501
clrPropAssign.Receiver,
502502
clrPropAssign.Value,
503-
(recv, val) => new BoundClrPropertyAssignmentExpression(null, recv, clrPropAssign.Member, val, clrPropAssign.Type));
503+
(recv, val) => new BoundClrPropertyAssignmentExpression(
504+
null,
505+
recv,
506+
clrPropAssign.Member,
507+
val,
508+
clrPropAssign.Type,
509+
clrPropAssign.ConstrainedReceiverTypeParameter,
510+
clrPropAssign.ConstrainedInterfaceType));
504511
case BoundClrBinaryOperatorExpression clrBinary:
505512
// Issue #2388: preserve whichever of Method (imported CLR
506513
// type) / Function (nullable-lifted same-compilation

src/Core/CodeAnalysis/Lowering/SideEffectSpiller.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,9 @@ protected override BoundExpression RewriteClrPropertyAssignmentExpression(BoundC
274274
receiver,
275275
rewritten.Member,
276276
value,
277-
rewritten.Type);
277+
rewritten.Type,
278+
rewritten.ConstrainedReceiverTypeParameter,
279+
rewritten.ConstrainedInterfaceType);
278280

279281
return new BoundBlockExpression(rewritten.Syntax, statements.ToImmutable(), assignment);
280282
}

test/Compiler.Tests/Emit/Issue2514ImportedInterfaceConstraintMemberTests.cs

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,88 @@ func Main() {
210210
Run(consumerPath));
211211
}
212212

213+
[Fact]
214+
public void ConstrainedObjectInitializer_WithSpilledSetterValue_RunsAndVerifies()
215+
{
216+
const string source = """
217+
package Issue2741
218+
import System
219+
import System.Collections.Generic
220+
import Issue2514.Contracts
221+
222+
func Next(value string) string -> value + "!"
223+
224+
func Add[T IContract[string] class init()](items ICollection[T], names IEnumerable[string]) {
225+
for name in names {
226+
var item = T{Name: Next(name)}
227+
items.Add(item)
228+
}
229+
}
230+
231+
func Main() {
232+
var items = List[RefContract]()
233+
var names = List[string]()
234+
names.Add("Ada")
235+
names.Add("Grace")
236+
Add[RefContract](items, names)
237+
Console.WriteLine(items[0].Name)
238+
Console.WriteLine(items[1].Name)
239+
}
240+
""";
241+
242+
using var result = Compile(source, "exe");
243+
Assert.Equal("Ada!\nGrace!\n", Run(result.OutputPath));
244+
IlVerifier.Verify(result.OutputPath, additionalReferences: new[] { result.ContractsPath });
245+
}
246+
247+
[Fact]
248+
public void OrdinaryInterfaceReceiver_WithSpilledSetterValue_RunsAndVerifies()
249+
{
250+
const string source = """
251+
package Issue2741Control
252+
import System
253+
import Issue2514.Contracts
254+
255+
func Next(value string) string -> value + "!"
256+
func SetName(value IContract[string], name string) { value.Name = Next(name) }
257+
258+
func Main() {
259+
var value = RefContract()
260+
SetName(value, "ordinary")
261+
Console.WriteLine(value.Name)
262+
}
263+
""";
264+
265+
using var result = Compile(source, "exe");
266+
Assert.Equal("ordinary!\n", Run(result.OutputPath));
267+
IlVerifier.Verify(result.OutputPath, additionalReferences: new[] { result.ContractsPath });
268+
}
269+
270+
[Fact]
271+
public void ConstrainedObjectInitializer_WithAwaitedSetterValue_RunsAndVerifies()
272+
{
273+
const string source = """
274+
package Issue2741Async
275+
import System
276+
import System.Threading.Tasks
277+
import Issue2514.Contracts
278+
279+
async func Make[T IContract[string] class init()](name string) T {
280+
var pending = Task.FromResult[string](name + "!")
281+
return T{Name: await pending}
282+
}
283+
284+
func Main() {
285+
var value = Make[RefContract]("async").Result
286+
Console.WriteLine(value.Name)
287+
}
288+
""";
289+
290+
using var result = Compile(source, "exe");
291+
Assert.Equal("async!\n", Run(result.OutputPath));
292+
IlVerifier.Verify(result.OutputPath, additionalReferences: new[] { result.ContractsPath });
293+
}
294+
213295
[Theory]
214296
[InlineData(
215297
"func Bad[T IContract[string]](value T) string -> value.Missing",

0 commit comments

Comments
 (0)