diff --git a/src/QsCompiler/CommandLineTool/Commands/Build.cs b/src/QsCompiler/CommandLineTool/Commands/Build.cs index 963402f818..08d555e801 100644 --- a/src/QsCompiler/CommandLineTool/Commands/Build.cs +++ b/src/QsCompiler/CommandLineTool/Commands/Build.cs @@ -225,6 +225,7 @@ public static int Run(BuildOptions options, ConsoleLogger logger) { ProjectName = options.ProjectName, AssemblyConstants = assemblyConstants, + ForceRewriteStepExecution = options.ForceRewriteStepExecution, TargetPackageAssemblies = options.TargetSpecificDecompositions ?? Enumerable.Empty(), RuntimeCapability = options.RuntimeCapability, SkipMonomorphization = options.RuntimeCapability == RuntimeCapability.FullComputation && options.QirOutputFolder == null, diff --git a/src/QsCompiler/CommandLineTool/Commands/Diagnose.cs b/src/QsCompiler/CommandLineTool/Commands/Diagnose.cs index a39ec54c66..37f6aae02e 100644 --- a/src/QsCompiler/CommandLineTool/Commands/Diagnose.cs +++ b/src/QsCompiler/CommandLineTool/Commands/Diagnose.cs @@ -237,6 +237,7 @@ public static int Run(DiagnoseOptions options, ConsoleLogger logger) var loadOptions = new CompilationLoader.Configuration { AssemblyConstants = assemblyConstants, + ForceRewriteStepExecution = options.ForceRewriteStepExecution, TargetPackageAssemblies = options.TargetSpecificDecompositions ?? Enumerable.Empty(), RuntimeCapability = options.RuntimeCapability, SkipMonomorphization = options.RuntimeCapability == RuntimeCapability.FullComputation, diff --git a/src/QsCompiler/CommandLineTool/Options.cs b/src/QsCompiler/CommandLineTool/Options.cs index 125306fb51..f5ee8d2406 100644 --- a/src/QsCompiler/CommandLineTool/Options.cs +++ b/src/QsCompiler/CommandLineTool/Options.cs @@ -64,6 +64,13 @@ public class CompilationOptions : Options HelpText = "Additional properties to populate the AssemblyConstants dictionary with. Each item is expected to be of the form \"key:value\".")] public IEnumerable? AdditionalAssemblyProperties { get; set; } + [Option( + "force-rewrite-step-execution", + Required = false, + Default = false, + HelpText = "Specifies whether to execute rewrite steps even if their precondition is not satisfied. If this is the case, the transformation output will be ignored and the compilation fails.")] + public bool ForceRewriteStepExecution { get; set; } + [Option( "runtime", Required = false, diff --git a/src/QsCompiler/Compiler/CompilationLoader.cs b/src/QsCompiler/Compiler/CompilationLoader.cs index 2855095dc7..e35db42883 100644 --- a/src/QsCompiler/Compiler/CompilationLoader.cs +++ b/src/QsCompiler/Compiler/CompilationLoader.cs @@ -61,6 +61,13 @@ public struct Configuration /// public string? ProjectName; + /// + /// If set to true, forces all rewrite steps to execute, regardless of whether their precondition was satisfied. + /// If the precondition of a step is not satisfied, the transformation is executed but the output will be ignored, + /// and an error is generated, indicating a compilation failure. + /// + public bool ForceRewriteStepExecution; + /// /// If set to true, the syntax tree rewrite step that replaces all generation directives /// for all functor specializations is executed during compilation. @@ -547,11 +554,11 @@ public CompilationLoader(SourceLoader loadSources, ReferenceLoader loadReference { if (this.config.RuntimeCapability == null || this.config.RuntimeCapability == RuntimeCapability.FullComputation) { - this.logger?.Log(WarningCode.MissingEntryPoint, Array.Empty()); + this.LogAndUpdate(ref this.compilationStatus.Validation, WarningCode.MissingEntryPoint); } else { - this.LogAndUpdate(ref this.compilationStatus.Validation, ErrorCode.MissingEntryPoint, Array.Empty()); + this.LogAndUpdate(ref this.compilationStatus.Validation, ErrorCode.MissingEntryPoint); } } @@ -730,12 +737,20 @@ private void LogAndUpdate(ref Status current, Exception ex) /// /// Logs an error with the given error code and message parameters, and updates the status passed as reference accordingly. /// - private void LogAndUpdate(ref Status current, ErrorCode code, IEnumerable args) + private void LogAndUpdate(ref Status current, ErrorCode code, params string[] args) { this.logger?.Log(code, args); current = Status.Failed; } + /// + /// Logs an error with the given warning code and message parameters, and updates the status passed as reference accordingly. + /// + private void LogAndUpdate(ref Status current, WarningCode code, params string[] args) + { + this.logger?.Log(code, args); + } + /// /// Logs the given diagnostic and updates the status passed as reference accordingly. /// Adds the given diagnostic to the tracked load diagnostics. @@ -751,7 +766,7 @@ private void LogAndUpdateLoadDiagnostics(ref Status current, Diagnostic d) /// private void OnCompilerException(Exception ex) { - this.LogAndUpdate(ref this.compilationStatus.Validation, ErrorCode.UnexpectedCompilerException, Enumerable.Empty()); + this.LogAndUpdate(ref this.compilationStatus.Validation, ErrorCode.UnexpectedCompilerException); this.LogAndUpdate(ref this.compilationStatus.Validation, ex); } @@ -830,7 +845,7 @@ private void PrintLoadedRewriteSteps(IEnumerable rewriteSteps) /// private QsCompilation? ReplaceTargetSpecificImplementations(IEnumerable paths, Uri rewriteStepOrigin, int nrReferences) { - void LogError(ErrorCode errCode, string[] args) => this.LogAndUpdate(ref this.compilationStatus.TargetSpecificReplacements, errCode, args); + void LogError(ErrorCode errCode, params string[] args) => this.LogAndUpdate(ref this.compilationStatus.TargetSpecificReplacements, errCode, args); void LogException(Exception ex) => this.LogAndUpdate(ref this.compilationStatus.TargetSpecificReplacements, ex); (string, ImmutableArray)? LoadReferences(string path) @@ -842,12 +857,12 @@ private void PrintLoadedRewriteSteps(IEnumerable rewriteSteps) { return (path, loaded.Namespaces); } - LogError(ErrorCode.FailedToLoadTargetSpecificDecompositions, new[] { targetDll }); + LogError(ErrorCode.FailedToLoadTargetSpecificDecompositions, targetDll); return null; } catch (Exception ex) { - LogError(ErrorCode.InvalidPathToTargetSpecificDecompositions, new[] { path }); + LogError(ErrorCode.InvalidPathToTargetSpecificDecompositions, path); LogException(ex); return null; } @@ -857,7 +872,7 @@ private void PrintLoadedRewriteSteps(IEnumerable rewriteSteps) var combinedSuccessfully = References.CombineSyntaxTrees(out var replacements, additionalAssemblies: nrReferences, onError: LogError, natives); if (!combinedSuccessfully) { - LogError(ErrorCode.ConflictsInTargetSpecificDecompositions, Array.Empty()); + LogError(ErrorCode.ConflictsInTargetSpecificDecompositions); } var targetSpecificDecompositions = new QsCompilation(replacements, ImmutableArray.Empty); @@ -877,6 +892,7 @@ private Status ExecuteRewriteStep(LoadedStep rewriteStep, QsCompilation compilat rewriteStep.Name == "CSharpGeneration" && severity == DiagnosticSeverity.Information ? Informations.Code(InformationCode.CsharpGenerationGeneratedInfo) : null; + var messageSource = ProjectManager.MessageSource(rewriteStep.Origin); void LogDiagnostics(ref Status status) { try @@ -889,13 +905,11 @@ void LogDiagnostics(ref Status status) } catch { - this.LogAndUpdate(ref status, Warning(WarningCode.RewriteStepDiagnosticsGenerationFailed, rewriteStep.Name)); + this.LogAndUpdate(ref status, WarningCode.RewriteStepDiagnosticsGenerationFailed, rewriteStep.Name, messageSource); } } var status = Status.Succeeded; - var messageSource = ProjectManager.MessageSource(rewriteStep.Origin); - Diagnostic Warning(WarningCode code, params string[] args) => Warnings.LoadWarning(code, args, messageSource); try { transformed = compilation; @@ -903,8 +917,15 @@ void LogDiagnostics(ref Status status) if (preconditionFailed) { LogDiagnostics(ref status); - this.LogAndUpdate(ref status, Warning(WarningCode.PreconditionVerificationFailed, rewriteStep.Name, messageSource)); - return status; + if (this.config.ForceRewriteStepExecution) + { + this.LogAndUpdate(ref status, ErrorCode.PreconditionVerificationFailed, rewriteStep.Name, messageSource); + } + else + { + this.LogAndUpdate(ref status, WarningCode.PreconditionVerificationFailed, rewriteStep.Name, messageSource); + return status; + } } var transformationFailed = rewriteStep.ImplementsTransformation && (!rewriteStep.Transformation(compilation, out transformed) || transformed == null); @@ -950,7 +971,7 @@ private ImmutableDictionary LoadSourceFiles(IEnumerable sou this.compilationStatus.SourceFileLoading = 0; if (sources == null) { - this.LogAndUpdate(ref this.compilationStatus.SourceFileLoading, ErrorCode.SourceFilesMissing, Enumerable.Empty()); + this.LogAndUpdate(ref this.compilationStatus.SourceFileLoading, ErrorCode.SourceFilesMissing); } void OnException(Exception ex) => this.LogAndUpdate(ref this.compilationStatus.SourceFileLoading, ex); void OnDiagnostic(Diagnostic d) => this.LogAndUpdateLoadDiagnostics(ref this.compilationStatus.SourceFileLoading, d); @@ -972,7 +993,7 @@ private References LoadAssemblies(IEnumerable refs, bool loadTestNames, this.compilationStatus.ReferenceLoading = 0; if (refs == null) { - this.logger?.Log(WarningCode.ReferencesSetToNull, Enumerable.Empty()); + this.LogAndUpdate(ref this.compilationStatus.ReferenceLoading, WarningCode.ReferencesSetToNull); } void OnException(Exception ex) => this.LogAndUpdate(ref this.compilationStatus.ReferenceLoading, ex); void OnDiagnostic(Diagnostic d) => this.LogAndUpdateLoadDiagnostics(ref this.compilationStatus.ReferenceLoading, d); @@ -992,7 +1013,7 @@ private References LoadAssemblies(IEnumerable refs, bool loadTestNames, private bool WriteSyntaxTreeSerialization(MemoryStream ms) { void LogError() => this.LogAndUpdate( - ref this.compilationStatus.Serialization, ErrorCode.SerializationFailed, Enumerable.Empty()); + ref this.compilationStatus.Serialization, ErrorCode.SerializationFailed); void LogExceptionAndError(Exception ex) { @@ -1042,7 +1063,7 @@ void LogExceptionAndError(Exception ex) catch (Exception ex) { this.LogAndUpdate(ref this.compilationStatus.BinaryFormat, ex); - this.LogAndUpdate(ref this.compilationStatus.BinaryFormat, ErrorCode.GeneratingBinaryFailed, Enumerable.Empty()); + this.LogAndUpdate(ref this.compilationStatus.BinaryFormat, ErrorCode.GeneratingBinaryFailed); return null; } } @@ -1098,8 +1119,7 @@ bool CanBeIncluded(string dll) var csharpTree = MetadataGeneration.GenerateAssemblyMetadata(references.Where(r => r.Item3).Select(r => r.Item2)); foreach (var (dropped, _, _) in references.Where(r => !r.Item3)) { - var warning = Warnings.LoadWarning(WarningCode.ReferenceCannotBeIncludedInDll, new[] { dropped }, null); - this.LogAndUpdate(ref this.compilationStatus.DllGeneration, warning); + this.LogAndUpdate(ref this.compilationStatus.DllGeneration, WarningCode.ReferencesSetToNull, dropped); } var compilation = CodeAnalysis.CSharp.CSharpCompilation.Create( @@ -1126,7 +1146,7 @@ bool CanBeIncluded(string dll) catch (Exception ex) { this.LogAndUpdate(ref this.compilationStatus.DllGeneration, ex); - this.LogAndUpdate(ref this.compilationStatus.DllGeneration, ErrorCode.GeneratingDllFailed, Enumerable.Empty()); + this.LogAndUpdate(ref this.compilationStatus.DllGeneration, ErrorCode.GeneratingDllFailed); return null; } } diff --git a/src/QsCompiler/DataStructures/Diagnostics.fs b/src/QsCompiler/DataStructures/Diagnostics.fs index b17303b501..4d4221f5ab 100644 --- a/src/QsCompiler/DataStructures/Diagnostics.fs +++ b/src/QsCompiler/DataStructures/Diagnostics.fs @@ -318,9 +318,10 @@ type ErrorCode = | PreEvaluationFailed = 7111 | RewriteStepExecutionFailed = 7112 | PostconditionVerificationFailed = 7113 + // the corresponding warning code exists + | PreconditionVerificationFailed = 7202 | CsharpGenerationGeneratedError = 8001 - | PublishingPerfResultsFailed = 8101 | PerformanceTrackingFailed = 8102 | SyntaxTreeNotMonomorphized = 8103 @@ -377,6 +378,7 @@ type WarningCode = | UnresolvedItemsInGeneratedQs = 7101 | RewriteStepDiagnosticsGenerationFailed = 7201 + // the corresponding error code exists | PreconditionVerificationFailed = 7202 | RewriteStepLoadedViaReflection = 7203 | FailedToLoadRewriteStepViaReflection = 7204 @@ -877,9 +879,10 @@ type DiagnosticItem = "Executing the transformation for the compilation step \"{0}\" loaded from \"{1}\" failed." | ErrorCode.PostconditionVerificationFailed -> "The postcondition for the compilation step \"{0}\" loaded from \"{1}\" was not satisfied. The transformation has produced incorrect output and should be excluded from the compilation process." + | ErrorCode.PreconditionVerificationFailed -> + "The precondition for the compilation step \"{0}\" loaded from \"{1}\" was not met." | ErrorCode.CsharpGenerationGeneratedError -> "" - | ErrorCode.PublishingPerfResultsFailed -> "Performance results failed to be published at \"{0}\"." | ErrorCode.PerformanceTrackingFailed -> "Performance tracking failed with error \"{0}\"." | ErrorCode.SyntaxTreeNotMonomorphized -> diff --git a/src/QsCompiler/Tests.Compiler/QirTests.fs b/src/QsCompiler/Tests.Compiler/QirTests.fs index c168293fba..5100cb0230 100644 --- a/src/QsCompiler/Tests.Compiler/QirTests.fs +++ b/src/QsCompiler/Tests.Compiler/QirTests.fs @@ -201,7 +201,11 @@ let ``QIR expressions`` () = qirTest false "TestExpressions" [] let ``QIR targeting`` () = let compilerArgs = - [ "--runtime"; "BasicMeasurementFeedback" ] + [ + "--runtime" + "BasicMeasurementFeedback" + "--force-rewrite-step-execution" // to make sure the target specific transformation actually runs + ] |> Seq.append (compilerArgs true "TestTargeting") |> Seq.toArray