29
29
#include " swift/AST/NameLookup.h"
30
30
#include " swift/AST/ParameterList.h"
31
31
#include " swift/AST/PrettyStackTrace.h"
32
+ #include " swift/AST/ProtocolConformance.h"
32
33
#include " swift/AST/SubstitutionMap.h"
33
34
#include " swift/AST/TypeCheckerDebugConsumer.h"
34
35
#include " swift/Basic/Statistic.h"
@@ -1937,6 +1938,11 @@ Expr *ExprTypeCheckListener::appliedSolution(Solution &solution, Expr *expr) {
1937
1938
return expr;
1938
1939
}
1939
1940
1941
+ void ExprTypeCheckListener::preCheckFailed (Expr *expr) {}
1942
+ void ExprTypeCheckListener::constraintGenerationFailed (Expr *expr) {}
1943
+ void ExprTypeCheckListener::applySolutionFailed (Solution &solution,
1944
+ Expr *expr) {}
1945
+
1940
1946
void ParentConditionalConformance::diagnoseConformanceStack (
1941
1947
DiagnosticEngine &diags, SourceLoc loc,
1942
1948
ArrayRef<ParentConditionalConformance> conformances) {
@@ -1964,20 +1970,116 @@ bool GenericRequirementsCheckListener::diagnoseUnsatisfiedRequirement(
1964
1970
return false ;
1965
1971
}
1966
1972
1973
+ // / Sometimes constraint solver fails without producing any diagnostics,
1974
+ // / that leads to crashes down the line in AST Verifier or SILGen
1975
+ // / which, as a result, are much harder to figure out.
1976
+ // /
1977
+ // / This class is intended to guard against situations like that by
1978
+ // / keeping track of failures of different type-check phases, and
1979
+ // / emitting fallback fatal error if any of them fail without producing
1980
+ // / error diagnostic, and there were no errors emitted or scheduled to be
1981
+ // / emitted previously.
1982
+ class FallbackDiagnosticListener : public ExprTypeCheckListener {
1983
+ TypeChecker &TC;
1984
+ TypeCheckExprOptions Options;
1985
+ ExprTypeCheckListener *BaseListener;
1986
+
1987
+ public:
1988
+ FallbackDiagnosticListener (TypeChecker &TC, TypeCheckExprOptions options,
1989
+ ExprTypeCheckListener *base)
1990
+ : TC(TC), Options(options), BaseListener(base) {}
1991
+
1992
+ bool builtConstraints (ConstraintSystem &cs, Expr *expr) override {
1993
+ return BaseListener ? BaseListener->builtConstraints (cs, expr) : false ;
1994
+ }
1995
+
1996
+ Expr *foundSolution (Solution &solution, Expr *expr) override {
1997
+ return BaseListener ? BaseListener->foundSolution (solution, expr) : expr;
1998
+ }
1999
+
2000
+ Expr *appliedSolution (Solution &solution, Expr *expr) override {
2001
+ return BaseListener ? BaseListener->appliedSolution (solution, expr) : expr;
2002
+ }
2003
+
2004
+ void preCheckFailed (Expr *expr) override {
2005
+ if (BaseListener)
2006
+ BaseListener->preCheckFailed (expr);
2007
+ maybeProduceFallbackDiagnostic (expr);
2008
+ }
2009
+
2010
+ void constraintGenerationFailed (Expr *expr) override {
2011
+ if (BaseListener)
2012
+ BaseListener->constraintGenerationFailed (expr);
2013
+ maybeProduceFallbackDiagnostic (expr);
2014
+ }
2015
+
2016
+ void applySolutionFailed (Solution &solution, Expr *expr) override {
2017
+ if (BaseListener)
2018
+ BaseListener->applySolutionFailed (solution, expr);
2019
+
2020
+ if (hadAnyErrors ())
2021
+ return ;
2022
+
2023
+ // If solution involves invalid or incomplete conformances that's
2024
+ // a probable cause of failure to apply it without producing an error,
2025
+ // which is going to be diagnosed later, so let's not produce
2026
+ // fallback diagnostic in this case.
2027
+ if (llvm::any_of (
2028
+ solution.Conformances ,
2029
+ [](const std::pair<ConstraintLocator *, ProtocolConformanceRef>
2030
+ &conformance) -> bool {
2031
+ auto &ref = conformance.second ;
2032
+ return ref.isConcrete () && (ref.getConcrete ()->isInvalid () ||
2033
+ ref.getConcrete ()->isIncomplete ());
2034
+ }))
2035
+ return ;
2036
+
2037
+ maybeProduceFallbackDiagnostic (expr);
2038
+ }
2039
+
2040
+ private:
2041
+ bool hadAnyErrors () const { return TC.Context .Diags .hadAnyError (); }
2042
+
2043
+ void maybeProduceFallbackDiagnostic (Expr *expr) const {
2044
+ if (Options.contains (TypeCheckExprFlags::SubExpressionDiagnostics) ||
2045
+ Options.contains (TypeCheckExprFlags::SuppressDiagnostics))
2046
+ return ;
2047
+
2048
+ // Before producing fatal error here, let's check if there are any "error"
2049
+ // diagnostics already emitted or waiting to be emitted. Because they are
2050
+ // a better indication of the problem.
2051
+ if (!(hadAnyErrors () || TC.Context .hasDelayedConformanceErrors ()))
2052
+ TC.diagnose (expr->getLoc (), diag::failed_to_produce_diagnostic);
2053
+ }
2054
+ };
2055
+
1967
2056
#pragma mark High-level entry points
1968
2057
Type TypeChecker::typeCheckExpression (Expr *&expr, DeclContext *dc,
1969
2058
TypeLoc convertType,
1970
2059
ContextualTypePurpose convertTypePurpose,
1971
2060
TypeCheckExprOptions options,
1972
2061
ExprTypeCheckListener *listener,
1973
2062
ConstraintSystem *baseCS) {
2063
+ FallbackDiagnosticListener diagListener (*this , options, listener);
2064
+ return typeCheckExpressionImpl (expr, dc, convertType, convertTypePurpose,
2065
+ options, diagListener, baseCS);
2066
+ }
2067
+
2068
+ Type TypeChecker::typeCheckExpressionImpl (Expr *&expr, DeclContext *dc,
2069
+ TypeLoc convertType,
2070
+ ContextualTypePurpose convertTypePurpose,
2071
+ TypeCheckExprOptions options,
2072
+ ExprTypeCheckListener &listener,
2073
+ ConstraintSystem *baseCS) {
1974
2074
FrontendStatsTracer StatsTracer (Context.Stats , " typecheck-expr" , expr);
1975
2075
PrettyStackTraceExpr stackTrace (Context, " type-checking" , expr);
1976
2076
1977
2077
// First, pre-check the expression, validating any types that occur in the
1978
2078
// expression and folding sequence expressions.
1979
- if (preCheckExpression (expr, dc))
2079
+ if (preCheckExpression (expr, dc)) {
2080
+ listener.preCheckFailed (expr);
1980
2081
return Type ();
2082
+ }
1981
2083
1982
2084
// Construct a constraint system from this expression.
1983
2085
ConstraintSystemOptions csOptions = ConstraintSystemFlags::AllowFixes;
@@ -2032,10 +2134,9 @@ Type TypeChecker::typeCheckExpression(Expr *&expr, DeclContext *dc,
2032
2134
convertTo = getOptionalType (expr->getLoc (), var);
2033
2135
}
2034
2136
2035
- // Attempt to solve the constraint system.
2036
2137
SmallVector<Solution, 4 > viable;
2037
- if (cs. solve (expr, convertTo, listener, viable,
2038
- allowFreeTypeVariables))
2138
+ // Attempt to solve the constraint system.
2139
+ if (cs. solve (expr, convertTo, &listener, viable, allowFreeTypeVariables))
2039
2140
return Type ();
2040
2141
2041
2142
// If the client allows the solution to have unresolved type expressions,
@@ -2049,11 +2150,9 @@ Type TypeChecker::typeCheckExpression(Expr *&expr, DeclContext *dc,
2049
2150
2050
2151
auto result = expr;
2051
2152
auto &solution = viable[0 ];
2052
- if (listener) {
2053
- result = listener->foundSolution (solution, result);
2054
- if (!result)
2055
- return Type ();
2056
- }
2153
+ result = listener.foundSolution (solution, result);
2154
+ if (!result)
2155
+ return Type ();
2057
2156
2058
2157
if (options.contains (TypeCheckExprFlags::SkipApplyingSolution))
2059
2158
return solution.simplifyType (cs.getType (expr));
@@ -2063,18 +2162,17 @@ Type TypeChecker::typeCheckExpression(Expr *&expr, DeclContext *dc,
2063
2162
solution, result, convertType.getType (),
2064
2163
options.contains (TypeCheckExprFlags::IsDiscarded),
2065
2164
options.contains (TypeCheckExprFlags::SkipMultiStmtClosures));
2165
+
2066
2166
if (!result) {
2167
+ listener.applySolutionFailed (solution, expr);
2067
2168
// Failure already diagnosed, above, as part of applying the solution.
2068
2169
return Type ();
2069
2170
}
2070
2171
2071
- // If there's a listener, notify it that we've applied the solution.
2072
- if (listener) {
2073
- result = listener->appliedSolution (solution, result);
2074
- if (!result) {
2075
- return Type ();
2076
- }
2077
- }
2172
+ // Notify listener that we've applied the solution.
2173
+ result = listener.appliedSolution (solution, result);
2174
+ if (!result)
2175
+ return Type ();
2078
2176
2079
2177
if (getLangOpts ().DebugConstraintSolver ) {
2080
2178
auto &log = Context.TypeCheckerDebug ->getStream ();
0 commit comments