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
11 changes: 6 additions & 5 deletions docs/features/refout.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,10 @@ The `/refout` parameter specifies a file path where the ref assembly should be o
The `/refonly` parameter is a flag that indicates that a ref assembly should be output instead of an implementation assembly.
The `/refonly` parameter is not allowed together with the `/refout` parameter, as it doesn't make sense to have both the primary and secondary outputs be ref assemblies. Also, the `/refonly` parameter silently disables outputting PDBs, as ref assemblies cannot be executed.
The `/refonly` parameter translates to `EmitMetadataOnly` being `true`, and `IncludePrivateMembers` being `false` in the `Emit` API (see details below).
Neither `/refonly` nor `/refout` are permitted with `/target:module` or `/addmodule` options.

When the compiler produces documentation, the contents produced will match the APIs that go into the primary output. In other words, the documentation will be filtered down when using the `/refonly` parameter.
Neither `/refonly` nor `/refout` are permitted with `/target:module`, `/addmodule`, or `/link:...` options.

The compilation from the command-line will either produce both assemblies (implementation and ref) or neither. There is no "partial success" scenario.
When the compiler produces documentation, it is un-affected by either the `/refonly` or `/refout` parameters.

### CscTask/CoreCompile
The `CoreCompile` target will support a new output, called `IntermediateRefAssembly`, which parallels the existing `IntermediateAssembly`.
Expand All @@ -65,8 +64,10 @@ Going back to the 4 driving scenarios:

## Future
As mentioned above, there may be further refinements after C# 7.1:
- controlling internals (producing public ref assemblies)
- produce ref assemblies even when there are errors outside method bodies (emitting error types when `EmitOptions.TolerateErrors` is set)
- Controlling internals (producing public ref assemblies)
- Produce ref assemblies even when there are errors outside method bodies (emitting error types when `EmitOptions.TolerateErrors` is set)
- When the compiler produces documentation, the contents produced could be filtered down to match the APIs that go into the primary output. In other words, the documentation could be filtered down when using the `/refonly` parameter.


## Open questions
- should explicit method implementations be included in ref assemblies?
Expand Down
9 changes: 9 additions & 0 deletions src/Compilers/CSharp/Portable/CSharpResources.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions src/Compilers/CSharp/Portable/CSharpResources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -2300,6 +2300,9 @@ If such a class is used as a base class and if the deriving class defines a dest
<data name="ERR_NoNetModuleOutputWhenRefOutOrRefOnly" xml:space="preserve">
<value>Cannot compile net modules when using /refout or /refonly.</value>
</data>
<data name="ERR_NoEmbeddedTypeWhenRefOutOrRefOnly" xml:space="preserve">
<value>Cannot embed types when using /refout or /refonly.</value>
</data>
<data name="ERR_OvlOperatorExpected" xml:space="preserve">
<value>Overloadable operator expected</value>
</data>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1178,6 +1178,11 @@ internal sealed override CommandLineArguments CommonParse(IEnumerable<string> ar
AddDiagnostic(diagnostics, diagnosticOptions, ErrorCode.ERR_NoRefOutWhenRefOnly);
}

if ((refOnly || outputRefFilePath != null) && metadataReferences.Any(r => r.Properties.EmbedInteropTypes))
{
AddDiagnostic(diagnostics, diagnosticOptions, ErrorCode.ERR_NoEmbeddedTypeWhenRefOutOrRefOnly);
}

if (outputKind == OutputKind.NetModule && (refOnly || outputRefFilePath != null))
{
AddDiagnostic(diagnostics, diagnosticOptions, ErrorCode.ERR_NoNetModuleOutputWhenRefOutOrRefOnly);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,6 @@ internal virtual IEnumerable<EventSymbol> GetEventsToEmit()

foreach (var member in this.GetMembers())
{
// PROTOTYPE(refout) Do something here?
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Explicit interface implementations should be included. My understanding is that they are virtual, and we already concluded that all virtual methods should be included in ref assemblies. I'll add a test to lock that in.

if (member.Kind == SymbolKind.Method)
{
var method = (MethodSymbol)member;
Expand Down
11 changes: 6 additions & 5 deletions src/Compilers/CSharp/Portable/Errors/ErrorCode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1476,11 +1476,12 @@ internal enum ErrorCode
WRN_Experimental = 8305,
ERR_TupleInferredNamesNotAvailable = 8306,

ERR_NoRefOutWhenRefOnly = 8355,
ERR_NoNetModuleOutputWhenRefOutOrRefOnly = 8356,
ERR_NoRefOutWhenRefOnly = 8307,
ERR_NoNetModuleOutputWhenRefOutOrRefOnly = 8308,
ERR_NoEmbeddedTypeWhenRefOutOrRefOnly = 8309,

ERR_BadDynamicMethodArgDefaultLiteral = 9000,
ERR_DefaultLiteralNotValid = 9001,
WRN_DefaultInSwitch = 9002,
ERR_BadDynamicMethodArgDefaultLiteral = 8310,
ERR_DefaultLiteralNotValid = 8311,
WRN_DefaultInSwitch = 8312,
}
}
44 changes: 42 additions & 2 deletions src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2886,6 +2886,18 @@ public void ParseOut()
// error CS8301: Do not use refout when using refonly.
Diagnostic(ErrorCode.ERR_NoRefOutWhenRefOnly).WithLocation(1, 1));

parsedArgs = DefaultParse(new[] { @"/refout:ref.dll", "/link:b", "a.cs" }, baseDirectory);
parsedArgs.Errors.Verify(
// error CS8357: Cannot embed types when using /refout or /refonly.
Diagnostic(ErrorCode.ERR_NoEmbeddedTypeWhenRefOutOrRefOnly).WithLocation(1, 1)
);

parsedArgs = DefaultParse(new[] { "/refonly", "/link:b", "a.cs" }, baseDirectory);
parsedArgs.Errors.Verify(
// error CS8357: Cannot embed types when using /refout or /refonly.
Diagnostic(ErrorCode.ERR_NoEmbeddedTypeWhenRefOutOrRefOnly).WithLocation(1, 1)
);

parsedArgs = DefaultParse(new[] { "/refonly:incorrect", "a.cs" }, baseDirectory);
parsedArgs.Errors.Verify(
// error CS2007: Unrecognized option: '/refonly:incorrect'
Expand Down Expand Up @@ -9067,6 +9079,11 @@ public static void Main()
{
System.Console.Write(""Hello"");
}
/// <summary>Private method</summary>
private static void PrivateMethod()
{
System.Console.Write(""Private"");
}
}");

var outWriter = new StringWriter(CultureInfo.InvariantCulture);
Expand All @@ -9081,7 +9098,7 @@ public static void Main()

MetadataReaderUtils.VerifyPEMetadata(exe,
new[] { "TypeDefinition:<Module>", "TypeDefinition:C" },
new[] { "MethodDefinition:Void Main()", "MethodDefinition:Void .ctor()" },
new[] { "MethodDefinition:Void Main()", "MethodDefinition:Void PrivateMethod()", "MethodDefinition:Void .ctor()" },
new[] { "CompilationRelaxationsAttribute", "RuntimeCompatibilityAttribute", "DebuggableAttribute" }
);

Expand All @@ -9099,6 +9116,9 @@ public static void Main()
<member name=""M:C.Main"">
<summary>Main method</summary>
</member>
<member name=""M:C.PrivateMethod"">
<summary>Private method</summary>
</member>
</members>
</doc>";
Assert.Equal(expectedDoc, content.Trim());
Expand Down Expand Up @@ -9170,6 +9190,16 @@ private event Action E1
remove { }
}
private event Action E2;

/// <summary>Private Class Field</summary>
private int field;

/// <summary>Private Struct</summary>
private struct S
{
/// <summary>Private Struct Field</summary>
private int field;
}
}");

var outWriter = new StringWriter(CultureInfo.InvariantCulture);
Expand All @@ -9185,7 +9215,7 @@ private event Action E1
// The types and members that are included needs further refinement.
// See issue https://github.com/dotnet/roslyn/issues/17612
MetadataReaderUtils.VerifyPEMetadata(refDll,
new[] { "TypeDefinition:<Module>", "TypeDefinition:C" },
new[] { "TypeDefinition:<Module>", "TypeDefinition:C", "TypeDefinition:S" },
new[] { "MethodDefinition:Void Main()", "MethodDefinition:Void .ctor()" },
new[] { "CompilationRelaxationsAttribute", "RuntimeCompatibilityAttribute", "DebuggableAttribute", "ReferenceAssemblyAttribute" }
);
Expand All @@ -9207,10 +9237,20 @@ private event Action E1
<member name=""M:C.Main"">
<summary>Main method</summary>
</member>
<member name=""F:C.field"">
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is unexpected. We don't eliminate private fields of a class?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Such fields are stripped from the ref assembly, but not from the doc output. I'll take a stab tomorrow to strip it out.

<summary>Private Class Field</summary>
</member>
<member name=""T:C.S"">
<summary>Private Struct</summary>
</member>
<member name=""F:C.S.field"">
<summary>Private Struct Field</summary>
</member>
</members>
</doc>";
Assert.Equal(expectedDoc, content.Trim());


// Clean up temp files
CleanupAllGeneratedFiles(dir.Path);
}
Expand Down
Loading