From 4b104f86f5e90d954229fa623c9508de1242c987 Mon Sep 17 00:00:00 2001 From: Charles Stoner Date: Wed, 15 Dec 2021 20:54:06 -0800 Subject: [PATCH] Report error if 'record struct' constructor calls default parameterless constructor (#58339) --- .../Portable/Binder/Binder_Statements.cs | 11 +- .../CSharp/Portable/CSharpResources.resx | 3 + .../CSharp/Portable/Errors/ErrorCode.cs | 2 + .../Portable/xlf/CSharpResources.cs.xlf | 5 + .../Portable/xlf/CSharpResources.de.xlf | 5 + .../Portable/xlf/CSharpResources.es.xlf | 5 + .../Portable/xlf/CSharpResources.fr.xlf | 5 + .../Portable/xlf/CSharpResources.it.xlf | 5 + .../Portable/xlf/CSharpResources.ja.xlf | 5 + .../Portable/xlf/CSharpResources.ko.xlf | 5 + .../Portable/xlf/CSharpResources.pl.xlf | 5 + .../Portable/xlf/CSharpResources.pt-BR.xlf | 5 + .../Portable/xlf/CSharpResources.ru.xlf | 5 + .../Portable/xlf/CSharpResources.tr.xlf | 5 + .../Portable/xlf/CSharpResources.zh-Hans.xlf | 5 + .../Portable/xlf/CSharpResources.zh-Hant.xlf | 5 + .../Semantic/Semantics/RecordStructTests.cs | 344 ++++++++++++++++++ 17 files changed, 424 insertions(+), 1 deletion(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs index bf9d9b659e10d..31869e224e126 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs @@ -3420,7 +3420,7 @@ private BoundNode BindConstructorBody(ConstructorDeclarationSyntax constructor, bool thisInitializer = initializer?.IsKind(SyntaxKind.ThisConstructorInitializer) == true; if (!thisInitializer && - ContainingType.GetMembersUnordered().OfType().Any()) + hasAnyRecordConstructors()) { var constructorSymbol = (MethodSymbol)this.ContainingMember(); if (!constructorSymbol.IsStatic && @@ -3437,6 +3437,12 @@ private BoundNode BindConstructorBody(ConstructorDeclarationSyntax constructor, && thisInitializer && ContainingType.IsDefaultValueTypeConstructor(initializer); + if (skipInitializer && + hasAnyRecordConstructors()) + { + Error(diagnostics, ErrorCode.ERR_RecordStructConstructorCallsDefaultConstructor, initializer.ThisOrBaseKeyword); + } + // Using BindStatement to bind block to make sure we are reusing results of partial binding in SemanticModel return new BoundConstructorMethodBody(constructor, bodyBinder.GetDeclaredLocalsForScope(constructor), @@ -3447,6 +3453,9 @@ private BoundNode BindConstructorBody(ConstructorDeclarationSyntax constructor, null : bodyBinder.BindExpressionBodyAsBlock(constructor.ExpressionBody, constructor.Body == null ? diagnostics : BindingDiagnosticBag.Discarded)); + + bool hasAnyRecordConstructors() => + ContainingType.GetMembersUnordered().OfType().Any(); } internal virtual BoundExpressionStatement BindConstructorInitializer(ConstructorInitializerSyntax initializer, BindingDiagnosticBag diagnostics) diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index d3cdb466a752d..c257fea3b7bd1 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -6866,6 +6866,9 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ A lambda expression with attributes cannot be converted to an expression tree + + A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. + The operation may overflow '{0}' at runtime (use 'unchecked' syntax to override) diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index 21d55131f01bf..98abc1cee973f 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -2000,6 +2000,8 @@ internal enum ErrorCode WRN_CompileTimeCheckedOverflow = 8973, WRN_MethGrpToNonDel = 8974, + ERR_RecordStructConstructorCallsDefaultConstructor = 8982, + #endregion // Note: you will need to re-generate compiler code after adding warnings (eng\generate-compiler-code.cmd) diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index 0fc0af7751f66..d334071e2d152 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -1022,6 +1022,11 @@ Primární konstruktor je v konfliktu se syntetizovaně zkopírovaným konstruktorem. + + A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. + A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. + + Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'. Přiřazení odkazu {1} k {0} nelze provést, protože {1} má užší řídicí obor než {0}. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index fab99a6112ed4..358f243c0c509 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -1022,6 +1022,11 @@ Der primäre Konstruktor verursacht einen Konflikt mit dem synthetisierten Kopierkonstruktor. + + A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. + A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. + + Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'. ref-assign von "{1}" zu "{0}" ist nicht möglich, weil "{1}" einen geringeren Escapebereich als "{0}" aufweist. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index e61b82ee62bd9..c029628899a86 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -1022,6 +1022,11 @@ El constructor principal está en conflicto con el constructor de copia sintetizado. + + A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. + A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. + + Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'. No se puede asignar referencia "{1}" a "{0}" porque "{1}" tiene un ámbito de escape más limitado que "{0}". diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index d3e10858683d6..54f9dfccbeb5c 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -1022,6 +1022,11 @@ Le constructeur principal est en conflit avec le constructeur de copie synthétisée. + + A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. + A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. + + Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'. Impossible d'effectuer une assignation par référence de '{1}' vers '{0}', car '{1}' a une portée de sortie plus limitée que '{0}'. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index ae09f873ca7a5..9884be5d98434 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -1022,6 +1022,11 @@ Il costruttore primario è in conflitto con il costruttore di copia sintetizzato. + + A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. + A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. + + Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'. Non è possibile assegnare '{1}' a '{0}' come ref perché l'ambito di escape di '{1}' è ridotto rispetto a quello di '{0}'. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index 78f8ef3c14a9d..832e8afce59f6 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -1022,6 +1022,11 @@ プライマリ コンストラクターが、合成されたコピー コンストラクターと競合しています。 + + A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. + A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. + + Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'. '{1}' を '{0}' に ref 割り当てすることはできません。'{1}' のエスケープ スコープが '{0}' より狭いためです。 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index f44cf74c02950..0980e056007d9 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -1022,6 +1022,11 @@ 기본 생성자가 합성된 복사 생성자와 충돌합니다. + + A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. + A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. + + Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'. '{1}'을(를) '{0}'에 참조 할당할 수 없습니다. '{1}'이(가) '{0}'보다 이스케이프 범위가 좁기 때문입니다. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index 6d5b28c290a26..e790851286280 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -1022,6 +1022,11 @@ Konstruktor podstawowy powoduje konflikt z konstruktorem syntetyzowanej kopii. + + A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. + A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. + + Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'. Nie można przypisać odwołania elementu „{1}” do elementu „{0}”, ponieważ element „{1}” ma węższy zakres wyjścia niż element „{0}”. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index 34597e7def371..781f858282a8c 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -1022,6 +1022,11 @@ O construtor primário entra em conflito com o construtor de cópia sintetizado. + + A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. + A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. + + Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'. Não é possível atribuir ref '{1}' a '{0}' porque '{1}' tem um escopo de escape mais limitado que '{0}'. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index ab7c1967c0b75..90dbd691b6a59 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -1022,6 +1022,11 @@ Первичный конструктор конфликтует с синтезированным конструктором копий. + + A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. + A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. + + Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'. Не удается присвоить по ссылке "{1}" для "{0}", так как escape-область у "{1}" уже, чем у "{0}". diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index 5160e9e0f8812..c3bdceded066e 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -1022,6 +1022,11 @@ Birincil oluşturucu, sentezlenmiş kopya oluşturucusuyla çakışıyor. + + A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. + A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. + + Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'. '{1}', '{0}' öğesinden daha dar bir kaçış kapsamı içerdiğinden '{0}' öğesine '{1}' ref ataması yapılamıyor. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index 8dd99d7d68f16..13572a101628a 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -1022,6 +1022,11 @@ 主构造函数与合成的复制构造函数冲突。 + + A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. + A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. + + Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'. 无法将“{1}”重新赋值为“{0}”,因为“{1}”具有比“{0}”更窄的转义范围。 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index 15fb46f8f8213..06d23b0f04b18 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -1022,6 +1022,11 @@ 主要建構函式與合成的複製建構函式相衝突。 + + A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. + A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. + + Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'. 不能將 '{1}' 參考指派至 '{0}',因為 '{1}' 的逸出範圍比 '{0}' 還要窄。 diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RecordStructTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RecordStructTests.cs index e5ae5daaf864a..fa11e107a789a 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RecordStructTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RecordStructTests.cs @@ -10616,5 +10616,349 @@ public Value(Value original) : this(42) { } var comp = CreateCompilation(src); CompileAndVerify(comp, expectedOutput: "Value { I = 42 }"); } + + [Fact] + public void ExplicitConstructors_01() + { + var source = +@"using static System.Console; +record struct S1 +{ +} +record struct S2 +{ + public S2() { } +} +record struct S3 +{ + public S3(object o) { } +} +class Program +{ + static void Main() + { + WriteLine(new S1()); + WriteLine(new S2()); + WriteLine(new S3()); + WriteLine(new S3(null)); + } +}"; + var verifier = CompileAndVerify(source, expectedOutput: +@"S1 { } +S2 { } +S3 { } +S3 { } +"); + verifier.VerifyMissing("S1..ctor()"); + verifier.VerifyIL("S2..ctor()", +@"{ + // Code size 1 (0x1) + .maxstack 0 + IL_0000: ret +}"); + verifier.VerifyMissing("S3..ctor()"); + verifier.VerifyIL("S3..ctor(object)", +@"{ + // Code size 1 (0x1) + .maxstack 0 + IL_0000: ret +}"); + } + + [Fact] + public void ExplicitConstructors_02() + { + var source = +@"record struct S1 +{ + public S1(object o) { } +} +record struct S2() +{ + public S2(object o) { } +} +record struct S3(char A) +{ + public S3(object o) { } +} +record struct S4(char A, char B) +{ + public S4(object o) { } +}"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (7,12): error CS8862: A constructor declared in a record with parameter list must have 'this' constructor initializer. + // public S2(object o) { } + Diagnostic(ErrorCode.ERR_UnexpectedOrMissingConstructorInitializerInRecord, "S2").WithLocation(7, 12), + // (11,12): error CS8862: A constructor declared in a record with parameter list must have 'this' constructor initializer. + // public S3(object o) { } + Diagnostic(ErrorCode.ERR_UnexpectedOrMissingConstructorInitializerInRecord, "S3").WithLocation(11, 12), + // (15,12): error CS8862: A constructor declared in a record with parameter list must have 'this' constructor initializer. + // public S4(object o) { } + Diagnostic(ErrorCode.ERR_UnexpectedOrMissingConstructorInitializerInRecord, "S4").WithLocation(15, 12)); + } + + [Fact] + public void ExplicitConstructors_03() + { + var source = +@"using static System.Console; +record struct S1 +{ + public S1(object o) : this() { } +} +record struct S2() +{ + public S2(object o) : this() { } +} +class Program +{ + static void Main() + { + WriteLine(new S1()); + WriteLine(new S2()); + } +}"; + CompileAndVerify(source, expectedOutput: +@"S1 { } +S2 { } +"); + } + + [Fact] + public void ExplicitConstructors_04() + { + var source = +@"using static System.Console; +record struct S0 +{ + internal object F = 0; + public S0() { } +} +record struct S1 +{ + internal object F = 1; + public S1(object o) : this() { F = o; } +} +record struct S2() +{ + internal object F = 2; + public S2(object o) : this() { F = o; } +} +class Program +{ + static void Main() + { + WriteLine(new S0().F); + WriteLine(new S1().F); + WriteLine(new S1(-1).F); + WriteLine(new S2().F); + WriteLine(new S2(-2).F); + } +}"; + CompileAndVerify(source, expectedOutput: +@"0 + +-1 +2 +-2 +"); + } + + [Fact] + [WorkItem(58328, "https://github.com/dotnet/roslyn/issues/58328")] + public void ExplicitConstructors_05() + { + var source = +@"record struct S3(char A) +{ + public S3(object o) : this() { } +} +record struct S4(char A, char B) +{ + public S4(object o) : this() { } +}"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (3,27): error CS8982: A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. + // public S3(object o) : this() { } + Diagnostic(ErrorCode.ERR_RecordStructConstructorCallsDefaultConstructor, "this").WithLocation(3, 27), + // (7,27): error CS8982: A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. + // public S4(object o) : this() { } + Diagnostic(ErrorCode.ERR_RecordStructConstructorCallsDefaultConstructor, "this").WithLocation(7, 27)); + } + + [Fact] + [WorkItem(58328, "https://github.com/dotnet/roslyn/issues/58328")] + public void ExplicitConstructors_06() + { + var source = +@"record struct S3(char A) +{ + internal object F = 3; + public S3(object o) : this() { F = o; } +} +record struct S4(char A, char B) +{ + internal object F = 4; + public S4(object o) : this() { F = o; } +}"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (4,27): error CS8982: A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. + // public S3(object o) : this() { F = o; } + Diagnostic(ErrorCode.ERR_RecordStructConstructorCallsDefaultConstructor, "this").WithLocation(4, 27), + // (9,27): error CS8982: A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. + // public S4(object o) : this() { F = o; } + Diagnostic(ErrorCode.ERR_RecordStructConstructorCallsDefaultConstructor, "this").WithLocation(9, 27)); + } + + [Fact] + public void ExplicitConstructors_07() + { + var source = +@"using static System.Console; +record struct S1 +{ + public S1(object o) : this() { } + public S1() { } +} +record struct S3(char A) +{ + public S3(object o) : this() { } + public S3() : this('a') { } +} +record struct S4(char A, char B) +{ + public S4(object o) : this() { } + public S4() : this('a', 'b') { } +} +class Program +{ + static void Main() + { + WriteLine(new S1()); + WriteLine(new S1(1)); + WriteLine(new S3()); + WriteLine(new S3(3)); + WriteLine(new S4()); + WriteLine(new S4(4)); + } +}"; + var verifier = CompileAndVerify(source, expectedOutput: +@"S1 { } +S1 { } +S3 { A = a } +S3 { A = a } +S4 { A = a, B = b } +S4 { A = a, B = b } +"); + verifier.VerifyIL("S1..ctor()", +@"{ + // Code size 1 (0x1) + .maxstack 0 + IL_0000: ret +}"); + verifier.VerifyIL("S1..ctor(object)", +@"{ + // Code size 7 (0x7) + .maxstack 1 + IL_0000: ldarg.0 + IL_0001: call ""S1..ctor()"" + IL_0006: ret +}"); + verifier.VerifyIL("S3..ctor()", +@"{ + // Code size 9 (0x9) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldc.i4.s 97 + IL_0003: call ""S3..ctor(char)"" + IL_0008: ret +}"); + verifier.VerifyIL("S3..ctor(object)", +@"{ + // Code size 7 (0x7) + .maxstack 1 + IL_0000: ldarg.0 + IL_0001: call ""S3..ctor()"" + IL_0006: ret +}"); + verifier.VerifyIL("S4..ctor()", +@"{ + // Code size 11 (0xb) + .maxstack 3 + IL_0000: ldarg.0 + IL_0001: ldc.i4.s 97 + IL_0003: ldc.i4.s 98 + IL_0005: call ""S4..ctor(char, char)"" + IL_000a: ret +}"); + verifier.VerifyIL("S4..ctor(object)", +@"{ + // Code size 7 (0x7) + .maxstack 1 + IL_0000: ldarg.0 + IL_0001: call ""S4..ctor()"" + IL_0006: ret +}"); + } + + [Fact] + public void ExplicitConstructors_08() + { + var source = +@"record struct S2() +{ + public S2(object o) : this() { } + public S2() { } +}"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (3,27): error CS2121: The call is ambiguous between the following methods or properties: 'S2.S2()' and 'S2.S2()' + // public S2(object o) : this() { } + Diagnostic(ErrorCode.ERR_AmbigCall, "this").WithArguments("S2.S2()", "S2.S2()").WithLocation(3, 27), + // (4,12): error CS2111: Type 'S2' already defines a member called 'S2' with the same parameter types + // public S2() { } + Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "S2").WithArguments("S2", "S2").WithLocation(4, 12), + // (4,12): error CS8862: A constructor declared in a record with parameter list must have 'this' constructor initializer. + // public S2() { } + Diagnostic(ErrorCode.ERR_UnexpectedOrMissingConstructorInitializerInRecord, "S2").WithLocation(4, 12)); + } + + [Fact] + public void ExplicitConstructors_09() + { + var source = +@"record struct S1 +{ + public S1(object o) : base() { } +} +record struct S2() +{ + public S2(object o) : base() { } +} +record struct S3(char A) +{ + public S3(object o) : base() { } +}"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (3,12): error CS0522: 'S1': structs cannot call base class constructors + // public S1(object o) : base() { } + Diagnostic(ErrorCode.ERR_StructWithBaseConstructorCall, "S1").WithArguments("S1").WithLocation(3, 12), + // (7,12): error CS0522: 'S2': structs cannot call base class constructors + // public S2(object o) : base() { } + Diagnostic(ErrorCode.ERR_StructWithBaseConstructorCall, "S2").WithArguments("S2").WithLocation(7, 12), + // (7,27): error CS8862: A constructor declared in a record with parameter list must have 'this' constructor initializer. + // public S2(object o) : base() { } + Diagnostic(ErrorCode.ERR_UnexpectedOrMissingConstructorInitializerInRecord, "base").WithLocation(7, 27), + // (11,12): error CS0522: 'S3': structs cannot call base class constructors + // public S3(object o) : base() { } + Diagnostic(ErrorCode.ERR_StructWithBaseConstructorCall, "S3").WithArguments("S3").WithLocation(11, 12), + // (11,27): error CS8862: A constructor declared in a record with parameter list must have 'this' constructor initializer. + // public S3(object o) : base() { } + Diagnostic(ErrorCode.ERR_UnexpectedOrMissingConstructorInitializerInRecord, "base").WithLocation(11, 27)); + } } }