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
2 changes: 2 additions & 0 deletions compiler/analyzer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2008,6 +2008,8 @@ InvokeEntry *analyzeCallable(ObjectPtr x, llvm::ArrayRef<PVData> args) {
matchFailureError(failures);
}

setCurrentOverload(entry->matchedOverload);

if (entry->parent->shouldLog)
matchFailureLog(failures);

Expand Down
45 changes: 22 additions & 23 deletions compiler/ceramic.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -609,6 +609,7 @@ struct CompileContextEntry {
Location location;

ObjectPtr callable;
OverloadPtr overload;

bool hasParams : 1;

Expand All @@ -633,6 +634,8 @@ void pushCompileContext(ObjectPtr obj, llvm::ArrayRef<ObjectPtr> params,

void popCompileContext();

void setCurrentOverload(OverloadPtr overload);

vector<CompileContextEntry> getCompileContext();

void setCompileContext(llvm::ArrayRef<CompileContextEntry> x);
Expand Down Expand Up @@ -1190,7 +1193,15 @@ enum BindingKind { BINDING_KIND_MAP(BINDING_KIND_GEN) };

llvm::raw_ostream &operator<<(llvm::raw_ostream &os, BindingKind);

struct PatternVar;
struct PatternVar {
IdentifierPtr name;
bool isMulti : 1;

PatternVar(bool isMulti, IdentifierPtr name)
: name(name), isMulti(isMulti) {}

PatternVar() : isMulti(false) {}
};

struct Binding : public Statement {
const BindingKind bindingKind;
Expand Down Expand Up @@ -1506,16 +1517,6 @@ struct ReturnSpec : public ANode {
: ANode(RETURN_SPEC), type(type), name(name) {}
};

struct PatternVar {
IdentifierPtr name;
bool isMulti : 1;

PatternVar(bool isMulti, IdentifierPtr name)
: name(name), isMulti(isMulti) {}

PatternVar() : isMulti(false) {}
};

struct LLVMCode : ANode {
const string body;

Expand Down Expand Up @@ -1696,26 +1697,24 @@ struct Overload : public TopLevelItem {
PatternPtr callablePattern;
vector<PatternPtr> argPatterns;
MultiPatternPtr varArgPattern;
InlineAttribute isInline : 3;
int patternsInitializedState : 2; // 0:notinit, -1:initing, +1:inited
bool callByName : 1;
bool nameIsPattern : 1;
bool hasAsConversion : 1;
bool isDefault : 1;
InlineAttribute isInline : 3 = IGNORE;
int patternsInitializedState : 2 = 0; // 0:notinit, -1:initing, +1:inited
bool callByName : 1 = false;
bool nameIsPattern : 1 = false;
bool hasAsConversion : 1 = false;
bool isDefault : 1 = false;
bool isDiagnosticTransparent : 1 = false;

Overload(Module *module, ExprPtr target, CodePtr code, bool callByName,
InlineAttribute isInline)
: TopLevelItem(OVERLOAD, module), target(target), code(code),
isInline(isInline), patternsInitializedState(0),
callByName(callByName), nameIsPattern(false), hasAsConversion(false),
isDefault(false) {}
isInline(isInline), callByName(callByName) {}

Overload(Module *module, ExprPtr target, CodePtr code, bool callByName,
InlineAttribute isInline, bool hasAsConversion)
: TopLevelItem(OVERLOAD, module), target(target), code(code),
isInline(isInline), patternsInitializedState(0),
callByName(callByName), nameIsPattern(false),
hasAsConversion(hasAsConversion), isDefault(false) {}
isInline(isInline), callByName(callByName),
hasAsConversion(hasAsConversion) {}
};

struct Procedure : public TopLevelItem {
Expand Down
134 changes: 114 additions & 20 deletions compiler/error.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "matchinvoke.hpp"
#include "printer.hpp"

#include <algorithm>
#include <cstdarg>

namespace ceramic {
Expand Down Expand Up @@ -48,6 +49,11 @@ void pushCompileContext(ObjectPtr obj, llvm::ArrayRef<ObjectPtr> params,

void popCompileContext() { contextStack.pop_back(); }

void setCurrentOverload(OverloadPtr overload) {
if (!contextStack.empty())
contextStack.back().overload = overload;
}

vector<CompileContextEntry> getCompileContext() { return contextStack; }

void setCompileContext(llvm::ArrayRef<CompileContextEntry> x) {
Expand Down Expand Up @@ -433,42 +439,130 @@ void matchBindingError(MatchResultPtr const &result) {
error(sout.str());
}

// Rank a match-failure kind by how informative it is to the user.
// Higher = more useful (predicate names, type-pattern mismatches).
// Lower = noise (this overload was just for an unrelated callable name).
static int failureScore(MatchCode code) {
switch (code) {
case MATCH_PREDICATE_ERROR:
return 4;
case MATCH_ARGUMENT_ERROR:
case MATCH_MULTI_ARGUMENT_ERROR:
case MATCH_BINDING_ERROR:
case MATCH_MULTI_BINDING_ERROR:
return 3;
case MATCH_ARITY_ERROR:
return 2;
case MATCH_CALLABLE_ERROR:
return 1;
default:
return 0;
}
}

static void printFailureLine(llvm::raw_ostream &sout,
const pair<OverloadPtr, MatchResultPtr> &failure) {
sout << "\n ";
Location location = failure.first->location;
unsigned line, column, tabColumn;
getLineCol(location, line, column, tabColumn);
sout << location.source->fileName.c_str() << "(" << line + 1 << ","
<< column << ")"
<< "\n ";
printMatchError(sout, failure.second);
}

static void matchFailureMessage(MatchFailureError const &err, string &outBuf) {
llvm::raw_string_ostream sout(outBuf);
int hiddenPatternOverloads = 0;

// -full-match-errors preserves the original verbatim dump.
if (shouldPrintFullMatchErrors) {
for (const auto &failure : err.failures)
printFailureLine(sout, failure);
sout.flush();
return;
}

// Default path: hide universal pattern overloads, then rank what's left.
int hiddenPatternOverloads = 0;
vector<pair<OverloadPtr, MatchResultPtr>> visible;
for (const auto &failure : err.failures) {
OverloadPtr overload = failure.first;
if (!shouldPrintFullMatchErrors && overload->nameIsPattern) {
if (failure.first->nameIsPattern) {
++hiddenPatternOverloads;
continue;
}
sout << "\n ";
Location location = overload->location;
unsigned line, column, tabColumn;
getLineCol(location, line, column, tabColumn);
sout << location.source->fileName.c_str() << "(" << line + 1 << ","
<< column << ")"
<< "\n ";
printMatchError(sout, failure.second);
visible.push_back(failure);
}
if (hiddenPatternOverloads > 0)

std::stable_sort(visible.begin(), visible.end(),
[](const pair<OverloadPtr, MatchResultPtr> &a,
const pair<OverloadPtr, MatchResultPtr> &b) {
return failureScore(a.second->matchCode) >
failureScore(b.second->matchCode);
});

const size_t MAX_SHOW = 5;
size_t shown = std::min(visible.size(), MAX_SHOW);
size_t lessSpecific = visible.size() - shown;

for (size_t i = 0; i < shown; ++i)
printFailureLine(sout, visible[i]);

if (lessSpecific > 0) {
sout << "\n " << lessSpecific
<< " other less-specific overloads not shown";
if (hiddenPatternOverloads > 0)
sout << " (plus " << hiddenPatternOverloads
<< " universal overloads)";
sout << " (use -full-match-errors for all)";
} else if (hiddenPatternOverloads > 0) {
sout << "\n " << hiddenPatternOverloads
<< " universal overloads not shown (show with -full-match-errors "
"option)";
<< " universal overloads not shown (use -full-match-errors for "
"all)";
}
sout.flush();
}

void matchFailureError(MatchFailureError const &err) {
string buf;
if (err.failedInterface)
buf = "call does not conform to function interface";
else if (err.ambiguousMatch)
buf = "call matches ambiguous overloads";
else
buf = "no matching overload found";
{
llvm::raw_string_ostream sout(buf);
if (err.failedInterface)
sout << "call does not conform to function interface";
else if (err.ambiguousMatch)
sout << "call matches ambiguous overloads";
else {
sout << "no matching overload found";
// Append the callable + arg types from the deepest context frame,
// e.g. "no matching overload found for +(Foo, Foo)"
if (!contextStack.empty()) {
const auto &top = contextStack.back();
sout << " for ";
printName(sout, top.callable);
if (top.hasParams) {
sout << "(";
printNameList(sout, top.params, top.dispatchIndices);
sout << ")";
}
}
}
sout.flush();
}

matchFailureMessage(err, buf);

// Pick the deepest blameable frame.
for (auto it = contextStack.rbegin(); it != contextStack.rend(); ++it) {
if (!it->location.ok() || !it->location.source)
continue;
if (it->overload.ptr() == nullptr)
continue;
if (it->overload->isDiagnosticTransparent)
continue;
pushLocation(it->location);
break;
}

error(buf);
}

Expand Down
1 change: 1 addition & 0 deletions compiler/invoketables.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,7 @@ static InvokeEntry *newInvokeEntry(InvokeSet *parent, MatchSuccessPtr match,
MatchSuccessPtr interfaceMatch) {
InvokeEntry *entry =
new InvokeEntry(parent, match->callable, match->argsKey);
entry->matchedOverload = match->overload;
entry->origCode = match->overload->code;
entry->code = clone(match->overload->code);
entry->env = match->env;
Expand Down
1 change: 1 addition & 0 deletions compiler/invoketables.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ struct InvokeEntry {

InvokeSet *parent;
ObjectPtr callable;
OverloadPtr matchedOverload;
vector<TypePtr> argsKey;
vector<uint8_t> forwardedRValueFlags;

Expand Down
Loading
Loading