|
14 | 14 | #include "swift/AST/DiagnosticsSIL.h"
|
15 | 15 | #include "swift/Demangling/Demangle.h"
|
16 | 16 | #include "swift/SIL/SILBuilder.h"
|
| 17 | +#include "llvm/ADT/DenseSet.h" |
17 | 18 | #include "llvm/Support/TrailingObjects.h"
|
18 | 19 | using namespace swift;
|
19 | 20 |
|
@@ -567,74 +568,116 @@ SymbolicValue SymbolicValue::lookThroughSingleElementAggregates() const {
|
567 | 568 | }
|
568 | 569 | }
|
569 | 570 |
|
570 |
| -/// Emits an explanatory note if there is useful information to note or if there |
571 |
| -/// is an interesting SourceLoc to point at. |
572 |
| -/// Returns true if a diagnostic was emitted. |
573 |
| -static bool emitNoteDiagnostic(SILInstruction *badInst, UnknownReason reason, |
574 |
| - SILLocation fallbackLoc) { |
575 |
| - auto loc = skipInternalLocations(badInst->getDebugLocation()).getLocation(); |
576 |
| - if (loc.isNull()) { |
577 |
| - // If we have important clarifying information, make sure to emit it. |
578 |
| - if (reason == UnknownReason::Default || fallbackLoc.isNull()) |
579 |
| - return false; |
580 |
| - loc = fallbackLoc; |
| 571 | +/// Given that this is an 'Unknown' value, emit diagnostic notes providing |
| 572 | +/// context about what the problem is. Specifically, point to interesting |
| 573 | +/// source locations and function calls in the call stack. |
| 574 | +void SymbolicValue::emitUnknownDiagnosticNotes(SILLocation fallbackLoc) { |
| 575 | + auto unknownNode = getUnknownNode(); |
| 576 | + auto unknownReason = getUnknownReason(); |
| 577 | + auto errorCallStack = getUnknownCallStack(); |
| 578 | + |
| 579 | + ASTContext &ctx = unknownNode->getModule()->getASTContext(); |
| 580 | + |
| 581 | + // Extract the location of the instruction/construct that triggered the error |
| 582 | + // during interpretation, if available. |
| 583 | + Optional<SourceLoc> triggerLoc = None; |
| 584 | + if (auto badInst = dyn_cast<SILInstruction>(unknownNode)) { |
| 585 | + triggerLoc = skipInternalLocations(badInst->getDebugLocation()) |
| 586 | + .getLocation() |
| 587 | + .getSourceLoc(); |
| 588 | + } |
| 589 | + |
| 590 | + // Determine the top-level expression where the error happens and use it as |
| 591 | + // the location to emit diagnostics. Specifically, if the call-stack is |
| 592 | + // non-empty, use the first call in the sequence as the error location as the |
| 593 | + // error happens only in the context of this call. Use the fallback loc if |
| 594 | + // the faulty top-level expression location cannot be found. |
| 595 | + auto diagLoc = |
| 596 | + errorCallStack.empty() |
| 597 | + ? (triggerLoc ? triggerLoc.getValue() : fallbackLoc.getSourceLoc()) |
| 598 | + : errorCallStack.front(); |
| 599 | + if (diagLoc.isInvalid()) { |
| 600 | + return; |
581 | 601 | }
|
582 | 602 |
|
583 |
| - auto &ctx = badInst->getModule().getASTContext(); |
584 |
| - auto sourceLoc = loc.getSourceLoc(); |
585 |
| - switch (reason) { |
| 603 | + // Emit a note at the trigger location as well if it is different from the |
| 604 | + // top-level expression. |
| 605 | + bool emitTriggerLocInDiag = |
| 606 | + triggerLoc ? diagLoc != triggerLoc.getValue() : false; |
| 607 | + |
| 608 | + switch (unknownReason) { |
586 | 609 | case UnknownReason::Default:
|
587 |
| - diagnose(ctx, sourceLoc, diag::constexpr_unknown_reason_default) |
588 |
| - .highlight(loc.getSourceRange()); |
589 |
| - break; |
| 610 | + diagnose(ctx, diagLoc, diag::constexpr_unknown_reason_default); |
| 611 | + if (emitTriggerLocInDiag) |
| 612 | + diagnose(ctx, *triggerLoc, diag::constexpr_unevaluable_operation); |
| 613 | + return; |
590 | 614 | case UnknownReason::TooManyInstructions:
|
591 |
| - // TODO: Should pop up a level of the stack trace. |
592 |
| - diagnose(ctx, sourceLoc, diag::constexpr_too_many_instructions, |
593 |
| - ConstExprLimit) |
594 |
| - .highlight(loc.getSourceRange()); |
595 |
| - break; |
| 615 | + diagnose(ctx, diagLoc, diag::constexpr_too_many_instructions, |
| 616 | + ConstExprLimit); |
| 617 | + if (emitTriggerLocInDiag) |
| 618 | + diagnose(ctx, *triggerLoc, diag::constexpr_limit_exceeding_instruction); |
| 619 | + return; |
596 | 620 | case UnknownReason::Loop:
|
597 |
| - diagnose(ctx, sourceLoc, diag::constexpr_loop) |
598 |
| - .highlight(loc.getSourceRange()); |
599 |
| - break; |
| 621 | + diagnose(ctx, diagLoc, diag::constexpr_loop_found_note); |
| 622 | + if (emitTriggerLocInDiag) |
| 623 | + diagnose(ctx, *triggerLoc, diag::constexpr_loop_instruction); |
| 624 | + return; |
600 | 625 | case UnknownReason::Overflow:
|
601 |
| - diagnose(ctx, sourceLoc, diag::constexpr_overflow) |
602 |
| - .highlight(loc.getSourceRange()); |
603 |
| - break; |
| 626 | + diagnose(ctx, diagLoc, diag::constexpr_overflow); |
| 627 | + if (emitTriggerLocInDiag) |
| 628 | + diagnose(ctx, *triggerLoc, diag::constexpr_overflow_operation); |
| 629 | + return; |
604 | 630 | case UnknownReason::Trap:
|
605 |
| - diagnose(ctx, sourceLoc, diag::constexpr_trap) |
606 |
| - .highlight(loc.getSourceRange()); |
607 |
| - break; |
608 |
| - } |
609 |
| - return true; |
610 |
| -} |
611 |
| - |
612 |
| -/// Given that this is an 'Unknown' value, emit diagnostic notes providing |
613 |
| -/// context about what the problem is. |
614 |
| -void SymbolicValue::emitUnknownDiagnosticNotes(SILLocation fallbackLoc) { |
615 |
| - auto badInst = dyn_cast<SILInstruction>(getUnknownNode()); |
616 |
| - if (!badInst) |
| 631 | + diagnose(ctx, diagLoc, diag::constexpr_trap); |
| 632 | + if (emitTriggerLocInDiag) |
| 633 | + diagnose(ctx, *triggerLoc, diag::constexpr_trap_operation); |
617 | 634 | return;
|
618 |
| - |
619 |
| - bool emittedFirstNote = emitNoteDiagnostic(badInst, getUnknownReason(), |
620 |
| - fallbackLoc); |
621 |
| - |
622 |
| - auto sourceLoc = fallbackLoc.getSourceLoc(); |
623 |
| - auto &module = badInst->getModule(); |
624 |
| - if (sourceLoc.isInvalid()) { |
625 |
| - diagnose(module.getASTContext(), sourceLoc, diag::constexpr_not_evaluable); |
| 635 | + case UnknownReason::InvalidOperandValue: |
| 636 | + diagnose(ctx, diagLoc, diag::constexpr_invalid_operand_seen); |
| 637 | + if (emitTriggerLocInDiag) |
| 638 | + diagnose(ctx, *triggerLoc, diag::constexpr_operand_invalid_here); |
| 639 | + return; |
| 640 | + case UnknownReason::NotTopLevelConstant: { |
| 641 | + // For top-level errors, trigger loc is better than diagLoc. |
| 642 | + auto loc = emitTriggerLocInDiag ? *triggerLoc : diagLoc; |
| 643 | + diagnose(ctx, loc, diag::constexpr_value_unknown_at_top_level); |
626 | 644 | return;
|
627 | 645 | }
|
628 |
| - for (auto &sourceLoc : llvm::reverse(getUnknownCallStack())) { |
629 |
| - // Skip unknown sources. |
630 |
| - if (!sourceLoc.isValid()) |
631 |
| - continue; |
632 |
| - |
633 |
| - auto diag = emittedFirstNote ? diag::constexpr_called_from |
634 |
| - : diag::constexpr_not_evaluable; |
635 |
| - diagnose(module.getASTContext(), sourceLoc, diag); |
636 |
| - emittedFirstNote = true; |
| 646 | + case UnknownReason::MutipleTopLevelWriters: { |
| 647 | + // For top-level errors, trigger loc is better than diagLoc. |
| 648 | + auto loc = emitTriggerLocInDiag ? *triggerLoc : diagLoc; |
| 649 | + diagnose(ctx, loc, diag::constexpr_multiple_writers_found_at_top_level); |
| 650 | + return; |
| 651 | + } |
| 652 | + case UnknownReason::UnsupportedInstruction: |
| 653 | + diagnose(ctx, diagLoc, diag::constexpr_unsupported_instruction_found); |
| 654 | + if (emitTriggerLocInDiag) |
| 655 | + diagnose(ctx, *triggerLoc, |
| 656 | + diag::constexpr_unsupported_instruction_found_here); |
| 657 | + return; |
| 658 | + case UnknownReason::CalleeImplementationUnknown: |
| 659 | + diagnose(ctx, diagLoc, diag::constexpr_unknown_function_called); |
| 660 | + if (emitTriggerLocInDiag) |
| 661 | + diagnose(ctx, *triggerLoc, diag::constexpr_unknown_function_called_here); |
| 662 | + return; |
| 663 | + case UnknownReason::UntrackedSILValue: |
| 664 | + diagnose(ctx, diagLoc, diag::constexpr_untracked_sil_value_use_found); |
| 665 | + if (emitTriggerLocInDiag) |
| 666 | + diagnose(ctx, *triggerLoc, diag::constexpr_untracked_sil_value_used_here); |
| 667 | + return; |
| 668 | + case UnknownReason::UnknownWitnessMethodConformance: |
| 669 | + diagnose(ctx, diagLoc, |
| 670 | + diag::constexpr_witness_call_with_no_conformance_found); |
| 671 | + if (emitTriggerLocInDiag) |
| 672 | + diagnose(ctx, *triggerLoc, diag::constexpr_witness_call_found_here); |
| 673 | + return; |
| 674 | + case UnknownReason::UnresolvableWitnessMethod: |
| 675 | + diagnose(ctx, diagLoc, diag::constexpr_witness_call_with_no_target_found); |
| 676 | + if (emitTriggerLocInDiag) |
| 677 | + diagnose(ctx, *triggerLoc, diag::constexpr_witness_call_found_here); |
| 678 | + return; |
637 | 679 | }
|
| 680 | + // TODO: print the call-stack in a controlled way if needed. |
638 | 681 | }
|
639 | 682 |
|
640 | 683 | /// Returns the element of `aggregate` specified by the access path.
|
|
0 commit comments