Skip to content

Commit c8f81a7

Browse files
committed
---
yaml --- r: 347629 b: refs/heads/master c: 178406c h: refs/heads/master i: 347627: 0e9a280
1 parent 1a90501 commit c8f81a7

File tree

17 files changed

+1442
-127
lines changed

17 files changed

+1442
-127
lines changed

[refs]

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
refs/heads/master: 0e60b633b58e48c255912dec1fb0356d6cb42d49
2+
refs/heads/master: 178406c9c2eff4787bc712370ca7e0c5a4bde455
33
refs/heads/master-next: 203b3026584ecad859eb328b2e12490099409cd5
44
refs/tags/osx-passed: b6b74147ef8a386f532cf9357a1bde006e552c54
55
refs/tags/swift-2.2-SNAPSHOT-2015-12-01-a: 6bb18e013c2284f2b45f5f84f2df2887dc0f7dea

trunk/include/swift/AST/DiagnosticsSIL.def

Lines changed: 48 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -339,16 +339,57 @@ ERROR(pound_assert_condition_not_constant,none,
339339
ERROR(pound_assert_failure,none,
340340
"%0", (StringRef))
341341

342-
NOTE(constexpr_unknown_reason_default,none, "could not fold operation", ())
342+
NOTE(constexpr_unknown_reason_default,none,
343+
"cannot evaluate expression as constant here", ())
344+
NOTE(constexpr_unevaluable_operation,none,
345+
"cannot constant evaluate operation", ())
346+
343347
NOTE(constexpr_too_many_instructions,none,
344-
"exceeded instruction limit: %0 when evaluating the expression at compile "
345-
"time", (unsigned))
346-
NOTE(constexpr_loop,none, "control flow loop found", ())
348+
"exceeded instruction limit: %0 when evaluating the expression "
349+
"at compile time", (unsigned))
350+
NOTE(constexpr_limit_exceeding_instruction,none, "limit exceeded here", ())
351+
352+
NOTE(constexpr_loop_found_note,none,
353+
"control-flow loop found during evaluation ", ())
354+
NOTE(constexpr_loop_instruction,none, "found loop here", ())
355+
347356
NOTE(constexpr_overflow,none, "integer overflow detected", ())
357+
NOTE(constexpr_overflow_operation,none, "operation overflows", ())
358+
348359
NOTE(constexpr_trap,none, "trap detected", ())
349-
NOTE(constexpr_called_from,none, "when called from here", ())
350-
NOTE(constexpr_not_evaluable,none,
351-
"expression not evaluable as constant here", ())
360+
NOTE(constexpr_trap_operation,none, "operation traps", ())
361+
362+
NOTE(constexpr_invalid_operand_seen, none,
363+
"operation with invalid operands encountered during evaluation",())
364+
NOTE(constexpr_operand_invalid_here, none,
365+
"operation with invalid operands encountered here",())
366+
367+
NOTE(constexpr_value_unknown_at_top_level,none,
368+
"cannot evaluate top-level value as constant here",())
369+
NOTE(constexpr_multiple_writers_found_at_top_level,none,
370+
"top-level value has multiple assignments",())
371+
372+
NOTE(constexpr_unsupported_instruction_found, none,
373+
"encountered operation not supported by the evaluator", ())
374+
NOTE(constexpr_unsupported_instruction_found_here,none,
375+
"operation not supported by the evaluator", ())
376+
377+
NOTE(constexpr_unknown_function_called, none,
378+
"encountered call to a function whose body is not available", ())
379+
NOTE(constexpr_unknown_function_called_here, none,
380+
"call to a function whose body is not available", ())
381+
382+
NOTE(constexpr_untracked_sil_value_use_found, none,
383+
"encountered use of a variable not tracked by the evaluator", ())
384+
NOTE(constexpr_untracked_sil_value_used_here, none,
385+
"untracked variable used here", ())
386+
387+
NOTE(constexpr_witness_call_with_no_conformance_found, none,
388+
"cannot find concrete conformance for a witness method call", ())
389+
NOTE(constexpr_witness_call_with_no_target_found, none,
390+
"cannot resolve a witness method call to a concrete function", ())
391+
NOTE(constexpr_witness_call_found_here, none,
392+
"witness method call found here", ())
352393

353394
ERROR(non_physical_addressof,none,
354395
"addressof only works with purely physical lvalues; "

trunk/include/swift/SIL/SILConstants.h

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,38 @@ enum class UnknownReason {
5858

5959
/// Unspecified trap detected.
6060
Trap,
61+
62+
/// An operation was applied over operands whose symbolic values were
63+
/// constants but were not valid for the operation.
64+
InvalidOperandValue,
65+
66+
/// Encountered an instruction not supported by the interpreter.
67+
UnsupportedInstruction,
68+
69+
/// Encountered a function call where the body of the called function is
70+
/// not available.
71+
CalleeImplementationUnknown,
72+
73+
/// Attempted to load from/store into a SIL value that was not tracked by
74+
/// the interpreter.
75+
UntrackedSILValue,
76+
77+
/// Attempted to find a concrete protocol conformance for a witness method
78+
/// and failed.
79+
UnknownWitnessMethodConformance,
80+
81+
/// Attempted to determine the SIL function of a witness method (based on a
82+
/// concrete protocol conformance) and failed.
83+
UnresolvableWitnessMethod,
84+
85+
/// The value of a top-level variable cannot be determined to be a constant.
86+
/// This is only relevant in the backward evaluation mode, which is used by
87+
/// #assert.
88+
NotTopLevelConstant,
89+
90+
/// A top-level value has multiple writers. This is only relevant in the
91+
/// non-flow-sensitive evaluation mode, which is used by #assert.
92+
MutipleTopLevelWriters,
6193
};
6294

6395
/// An abstract class that exposes functions for allocating symbolic values.

trunk/include/swift/SILOptimizer/PassManager/Passes.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,8 @@ PASS(ComputeLoopInfo, "compute-loop-info",
110110
"Compute Loop Information for Testing")
111111
PASS(ConditionForwarding, "condition-forwarding",
112112
"Conditional Branch Forwarding to Fold SIL switch_enum")
113+
PASS(ConstantEvaluatorTester, "test-constant-evaluator",
114+
"Test constant evaluator")
113115
PASS(CopyForwarding, "copy-forwarding",
114116
"Copy Forwarding to Remove Redundant Copies")
115117
PASS(CopyPropagation, "copy-propagation",

trunk/include/swift/SILOptimizer/Utils/ConstExpr.h

Lines changed: 53 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,17 +26,18 @@
2626

2727
#include "swift/Basic/LLVM.h"
2828
#include "swift/Basic/SourceLoc.h"
29+
#include "swift/SIL/SILBasicBlock.h"
30+
#include "llvm/ADT/SmallPtrSet.h"
2931

3032
namespace swift {
31-
class ApplyInst;
3233
class ASTContext;
3334
class Operand;
34-
class SILInstruction;
35+
class SILFunction;
3536
class SILModule;
3637
class SILNode;
37-
class SILValue;
3838
class SymbolicValue;
3939
class SymbolicValueAllocator;
40+
class ConstExprFunctionState;
4041
enum class UnknownReason;
4142

4243
/// This class is the main entrypoint for evaluating constant expressions. It
@@ -47,13 +48,14 @@ class ConstExprEvaluator {
4748
/// The current call stack, used for providing accurate diagnostics.
4849
llvm::SmallVector<SourceLoc, 4> callStack;
4950

50-
ConstExprEvaluator(const ConstExprEvaluator &) = delete;
5151
void operator=(const ConstExprEvaluator &) = delete;
5252

5353
public:
5454
explicit ConstExprEvaluator(SymbolicValueAllocator &alloc);
5555
~ConstExprEvaluator();
5656

57+
explicit ConstExprEvaluator(const ConstExprEvaluator &other);
58+
5759
SymbolicValueAllocator &getAllocator() { return allocator; }
5860

5961
void pushCallStack(SourceLoc loc) { callStack.push_back(loc); }
@@ -81,5 +83,52 @@ class ConstExprEvaluator {
8183
SmallVectorImpl<SymbolicValue> &results);
8284
};
8385

86+
/// A constant-expression evaluator that can be used to step through a control
87+
/// flow graph (SILFunction body) by evaluating one instruction at a time.
88+
class ConstExprStepEvaluator {
89+
private:
90+
ConstExprEvaluator evaluator;
91+
92+
ConstExprFunctionState *internalState;
93+
94+
unsigned stepsEvaluated = 0;
95+
96+
/// Targets of branches that were visited. This is used to detect loops during
97+
/// evaluation.
98+
SmallPtrSet<SILBasicBlock *, 8> visitedBlocks;
99+
100+
Optional<SymbolicValue>
101+
incrementStepsAndCheckLimit(SILInstruction *inst,
102+
bool includeInInstructionLimit);
103+
104+
ConstExprStepEvaluator(const ConstExprEvaluator &) = delete;
105+
void operator=(const ConstExprEvaluator &) = delete;
106+
107+
public:
108+
/// Constructs a step evaluator given an allocator and a non-null pointer to a
109+
/// SILFunction.
110+
explicit ConstExprStepEvaluator(SymbolicValueAllocator &alloc,
111+
SILFunction *fun);
112+
~ConstExprStepEvaluator();
113+
114+
/// Evaluate an instruction in the current interpreter state.
115+
/// \param instI instruction to be evaluated in the current interpreter state.
116+
/// \returns a pair where the first and second elements are defined as
117+
/// follows:
118+
/// The first element is the iterator to the next instruction from where
119+
/// the evaluation can continue, if the evaluation is successful.
120+
/// Otherwise, it is None.
121+
///
122+
/// Second element is None, if the evaluation is successful.
123+
/// Otherwise, is an unknown symbolic value that contains the error.
124+
std::pair<Optional<SILBasicBlock::iterator>, Optional<SymbolicValue>>
125+
evaluate(SILBasicBlock::iterator instI,
126+
bool includeInInstructionLimit = true);
127+
128+
Optional<SymbolicValue> lookupConstValue(SILValue value);
129+
130+
bool isKnownFunction(SILFunction *fun);
131+
};
132+
84133
} // end namespace swift
85134
#endif

trunk/lib/SIL/SILConstants.cpp

Lines changed: 100 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "swift/AST/DiagnosticsSIL.h"
1515
#include "swift/Demangling/Demangle.h"
1616
#include "swift/SIL/SILBuilder.h"
17+
#include "llvm/ADT/DenseSet.h"
1718
#include "llvm/Support/TrailingObjects.h"
1819
using namespace swift;
1920

@@ -567,74 +568,116 @@ SymbolicValue SymbolicValue::lookThroughSingleElementAggregates() const {
567568
}
568569
}
569570

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;
581601
}
582602

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) {
586609
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;
590614
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;
596620
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;
600625
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;
604630
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);
617634
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);
626644
return;
627645
}
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;
637679
}
680+
// TODO: print the call-stack in a controlled way if needed.
638681
}
639682

640683
/// Returns the element of `aggregate` specified by the access path.

0 commit comments

Comments
 (0)