Skip to content

Commit dd79e4f

Browse files
authored
[ConstExpr] Emit traceback when failing during nested call evaluation (#18123)
When emitting a constexpr-evaluation failure message, this change emits a trace of the calls the evaluator followed to generate the failure. To make the output easier to read, calls on the same source line as the failing location are elided from the output. So for example: func foo() -> Bool { print("Un-constant") return true } test.swift:6:1: error: #assert condition not constant ^ test.swift:2:9: note: expression not evaluable as constant here print("Un-constant") Notice there is no stack trace for the evaluation of the lambda.
1 parent 0b58383 commit dd79e4f

File tree

6 files changed

+231
-82
lines changed

6 files changed

+231
-82
lines changed

include/swift/AST/DiagnosticsSIL.def

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,9 @@ WARNING(designated_init_in_cross_module_extension,none,
221221
NOTE(designated_init_c_struct_fix,none,
222222
"use \"self.init()\" to initialize the struct with zero values", ())
223223

224+
NOTE(constexpr_called_from, none, "when called from here", ())
225+
NOTE(constexpr_not_evaluable, none,
226+
"expression not evaluable as constant here", ())
224227

225228
// SWIFT_ENABLE_TENSORFLOW
226229
// TensorFlow Support Diagnostics.
@@ -263,7 +266,6 @@ ERROR(tf_op_misuse, none,
263266
NOTE(tf_op_misuse_note, none,
264267
"%0", (StringRef))
265268

266-
267269
// Control flow diagnostics.
268270
ERROR(missing_return,none,
269271
"missing return in a %select{function|closure}1 expected to return %0",

include/swift/SIL/SILConstants.h

Lines changed: 18 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,13 @@ class SILValue;
2626
class SILBuilder;
2727
class SerializedSILLoader;
2828

29-
struct APIntSymbolicValue;
3029
struct APFloatSymbolicValue;
31-
struct EnumWithPayloadSymbolicValue;
30+
struct APIntSymbolicValue;
3231
struct ArraySymbolicValue;
3332
struct DerivedAddressValue;
33+
struct EnumWithPayloadSymbolicValue;
3434
struct SymbolicValueMemoryObject;
35+
struct UnknownSymbolicValue;
3536

3637
/// When we fail to constant fold a value, this captures a reason why,
3738
/// allowing the caller to produce a specific diagnostic. The "Unknown"
@@ -65,6 +66,7 @@ enum class UnknownReason {
6566
/// symbolic values (e.g. to save memory). It provides a simpler public
6667
/// interface though.
6768
class SymbolicValue {
69+
private:
6870
enum RepresentationKind {
6971
/// This value is an alloc stack that is has not (yet) been initialized
7072
/// by flow-sensitive analysis.
@@ -132,11 +134,9 @@ class SymbolicValue {
132134
};
133135

134136
union {
135-
/// When the value is Unknown, this contains the value that was the
137+
/// When the value is Unknown, this contains information about the
136138
/// unfoldable part of the computation.
137-
///
138-
/// TODO: make this a more rich representation.
139-
SILNode *unknown;
139+
UnknownSymbolicValue *unknown;
140140

141141
/// This is always a SILType with an object category. This is the value
142142
/// of the underlying instance type, not the MetatypeType.
@@ -268,23 +268,21 @@ class SymbolicValue {
268268
return kind != Unknown && kind != UninitMemory;
269269
}
270270

271-
static SymbolicValue getUnknown(SILNode *node, UnknownReason reason) {
272-
assert(node && "node must be present");
273-
SymbolicValue result;
274-
result.representationKind = RK_Unknown;
275-
result.value.unknown = node;
276-
result.aux.unknown_reason = reason;
277-
return result;
278-
}
271+
static SymbolicValue getUnknown(SILNode *node, UnknownReason reason,
272+
llvm::ArrayRef<SourceLoc> callStack,
273+
llvm::BumpPtrAllocator &allocator);
279274

275+
/// Return true if this represents an unknown result.
280276
bool isUnknown() const { return getKind() == Unknown; }
281277

282-
/// Return information about an unknown result, including the SIL node that
283-
/// is a problem, and the reason it is an issue.
284-
std::pair<SILNode *, UnknownReason> getUnknownValue() const {
285-
assert(representationKind == RK_Unknown);
286-
return {value.unknown, aux.unknown_reason};
287-
}
278+
/// Return the call stack for an unknown result.
279+
ArrayRef<SourceLoc> getUnknownCallStack() const;
280+
281+
/// Return the node that triggered an unknown result.
282+
SILNode *getUnknownNode() const;
283+
284+
/// Return the reason an unknown result was generated.
285+
UnknownReason getUnknownReason() const;
288286

289287
static SymbolicValue getUninitMemory() {
290288
SymbolicValue result;

lib/SIL/SILConstants.cpp

Lines changed: 127 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,8 @@ void SymbolicValue::print(llvm::raw_ostream &os, unsigned indent) const {
3636
os << "uninit\n";
3737
return;
3838
case RK_Unknown: {
39-
std::pair<SILNode *, UnknownReason> unknown = getUnknownValue();
40-
os << "unknown(" << (int)unknown.second << "): ";
41-
unknown.first->dump();
39+
os << "unknown(" << (int)getUnknownReason() << "): ";
40+
getUnknownNode()->dump();
4241
return;
4342
}
4443
case RK_Metatype:
@@ -485,6 +484,88 @@ ArrayRef<SymbolicValue> SymbolicValue::getAggregateValue() const {
485484
return ArrayRef<SymbolicValue>(value.aggregate, aux.aggregate_numElements);
486485
}
487486

487+
//===----------------------------------------------------------------------===//
488+
// Unknown
489+
//===----------------------------------------------------------------------===//
490+
491+
namespace swift {
492+
/// When the value is Unknown, this contains information about the unfoldable
493+
/// part of the computation.
494+
struct alignas(SourceLoc) UnknownSymbolicValue final
495+
: private llvm::TrailingObjects<UnknownSymbolicValue, SourceLoc> {
496+
friend class llvm::TrailingObjects<UnknownSymbolicValue, SourceLoc>;
497+
498+
/// The value that was unfoldable.
499+
SILNode *node;
500+
501+
/// A more explanatory reason for the value being unknown.
502+
UnknownReason reason;
503+
504+
/// The number of elements in the call stack.
505+
unsigned call_stack_size;
506+
507+
static UnknownSymbolicValue *create(SILNode *node, UnknownReason reason,
508+
ArrayRef<SourceLoc> elements,
509+
llvm::BumpPtrAllocator &allocator) {
510+
auto byteSize =
511+
UnknownSymbolicValue::totalSizeToAlloc<SourceLoc>(elements.size());
512+
auto rawMem = allocator.Allocate(byteSize, alignof(UnknownSymbolicValue));
513+
514+
// Placement-new the value inside the memory we just allocated.
515+
auto value = ::new (rawMem) UnknownSymbolicValue(
516+
node, reason, static_cast<unsigned>(elements.size()));
517+
std::uninitialized_copy(elements.begin(), elements.end(),
518+
value->getTrailingObjects<SourceLoc>());
519+
return value;
520+
}
521+
522+
ArrayRef<SourceLoc> getCallStack() const {
523+
return {getTrailingObjects<SourceLoc>(), call_stack_size};
524+
}
525+
526+
// This is used by the llvm::TrailingObjects base class.
527+
size_t numTrailingObjects(OverloadToken<SourceLoc>) const {
528+
return call_stack_size;
529+
}
530+
531+
private:
532+
UnknownSymbolicValue() = delete;
533+
UnknownSymbolicValue(const UnknownSymbolicValue &) = delete;
534+
UnknownSymbolicValue(SILNode *node, UnknownReason reason,
535+
unsigned call_stack_size)
536+
: node(node), reason(reason), call_stack_size(call_stack_size) {}
537+
};
538+
} // namespace swift
539+
540+
SymbolicValue SymbolicValue::getUnknown(SILNode *node, UnknownReason reason,
541+
llvm::ArrayRef<SourceLoc> callStack,
542+
llvm::BumpPtrAllocator &allocator) {
543+
auto *rawCallStack = allocator.Allocate<SourceLoc>(callStack.size());
544+
std::uninitialized_copy(callStack.begin(), callStack.end(), rawCallStack);
545+
546+
assert(node && "node must be present");
547+
SymbolicValue result;
548+
result.representationKind = RK_Unknown;
549+
result.value.unknown =
550+
UnknownSymbolicValue::create(node, reason, callStack, allocator);
551+
return result;
552+
}
553+
554+
ArrayRef<SourceLoc> SymbolicValue::getUnknownCallStack() const {
555+
assert(getKind() == Unknown);
556+
return value.unknown->getCallStack();
557+
}
558+
559+
SILNode *SymbolicValue::getUnknownNode() const {
560+
assert(getKind() == Unknown);
561+
return value.unknown->node;
562+
}
563+
564+
UnknownReason SymbolicValue::getUnknownReason() const {
565+
assert(getKind() == Unknown);
566+
return value.unknown->reason;
567+
}
568+
488569
//===----------------------------------------------------------------------===//
489570
// Enums
490571
//===----------------------------------------------------------------------===//
@@ -751,16 +832,35 @@ SymbolicValue SymbolicValue::lookThroughSingleElementAggregates() const {
751832
}
752833
}
753834

835+
/// Emits an explanatory note if there is useful information to note or if there
836+
/// is an interesting SourceLoc to point at.
837+
/// Returns true if a diagnostic was emitted.
838+
static bool emitNoteDiagnostic(SILInstruction *badInst, UnknownReason reason,
839+
SILLocation fallbackLoc, std::string error) {
840+
auto loc = skipInternalLocations(badInst->getDebugLocation()).getLocation();
841+
if (loc.isNull()) {
842+
// If we have important clarifying information, make sure to emit it.
843+
if (reason == UnknownReason::Default || fallbackLoc.isNull())
844+
return false;
845+
loc = fallbackLoc;
846+
}
847+
848+
auto &module = badInst->getModule();
849+
diagnose(module.getASTContext(), loc.getSourceLoc(), diag::tf_op_misuse_note,
850+
error)
851+
.highlight(loc.getSourceRange());
852+
return true;
853+
}
854+
754855
/// Given that this is an 'Unknown' value, emit diagnostic notes providing
755856
/// context about what the problem is.
756857
void SymbolicValue::emitUnknownDiagnosticNotes(SILLocation fallbackLoc) {
757-
std::pair<SILNode *, UnknownReason> unknown = getUnknownValue();
758-
auto badInst = dyn_cast<SILInstruction>(unknown.first);
858+
auto badInst = dyn_cast<SILInstruction>(getUnknownNode());
759859
if (!badInst)
760860
return;
761861

762862
std::string error;
763-
switch (unknown.second) {
863+
switch (getUnknownReason()) {
764864
case UnknownReason::Default:
765865
error = "could not fold operation";
766866
break;
@@ -779,17 +879,27 @@ void SymbolicValue::emitUnknownDiagnosticNotes(SILLocation fallbackLoc) {
779879
break;
780880
}
781881

782-
auto &module = badInst->getModule();
882+
bool emittedFirstNote =
883+
emitNoteDiagnostic(badInst, getUnknownReason(), fallbackLoc, error);
783884

784-
auto loc = skipInternalLocations(badInst->getDebugLocation()).getLocation();
785-
if (loc.isNull()) {
786-
// If we have important clarifying information, make sure to emit it.
787-
if (unknown.second == UnknownReason::Default || fallbackLoc.isNull())
788-
return;
789-
loc = fallbackLoc;
885+
auto &module = badInst->getModule();
886+
auto &SM = module.getASTContext().SourceMgr;
887+
unsigned originalDiagnosticLineNumber =
888+
SM.getLineNumber(fallbackLoc.getSourceLoc());
889+
for (auto &sourceLoc : llvm::reverse(getUnknownCallStack())) {
890+
// Skip known sources.
891+
if (!sourceLoc.isValid())
892+
continue;
893+
// Also skip notes that point to the same line as the original error, for
894+
// example in:
895+
// #assert(foo(bar()))
896+
// it is not useful to get three diagnostics referring to the same line.
897+
if (SM.getLineNumber(sourceLoc) == originalDiagnosticLineNumber)
898+
continue;
899+
900+
auto diag = emittedFirstNote ? diag::constexpr_called_from
901+
: diag::constexpr_not_evaluable;
902+
diagnose(module.getASTContext(), sourceLoc, diag);
903+
emittedFirstNote = true;
790904
}
791-
792-
diagnose(module.getASTContext(), loc.getSourceLoc(), diag::tf_op_misuse_note,
793-
error)
794-
.highlight(loc.getSourceRange());
795905
}

0 commit comments

Comments
 (0)