Skip to content

Commit b02b31f

Browse files
committed
Improved diagnostic of generic type equality constraint
1 parent 6531ed0 commit b02b31f

File tree

2 files changed

+44
-30
lines changed

2 files changed

+44
-30
lines changed

source/slang/slang-check-decl.cpp

Lines changed: 33 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -365,7 +365,7 @@ struct SemanticsDeclHeaderVisitor : public SemanticsDeclVisitorBase,
365365

366366
void visitGenericTypeConstraintDecl(GenericTypeConstraintDecl* decl);
367367

368-
bool checkGenericTypeEqualityCanonicalOrder(GenericTypeConstraintDecl* decl);
368+
void checkGenericTypeEqualityConstraintSubType(GenericTypeConstraintDecl* decl);
369369

370370
void visitTypeCoercionConstraintDecl(TypeCoercionConstraintDecl* decl);
371371

@@ -3258,6 +3258,20 @@ bool SemanticsDeclHeaderVisitor::validateGenericConstraintSubType(
32583258
TypeExp type,
32593259
DiagnosticSink* sink)
32603260
{
3261+
auto diagnose = [&]()
3262+
{
3263+
if (sink)
3264+
{
3265+
if (decl->isEqualityConstraint)
3266+
{
3267+
sink->diagnose(type.exp, Diagnostics::invalidEqualityConstraintSubType, type);
3268+
}
3269+
else
3270+
{
3271+
sink->diagnose(type.exp, Diagnostics::invalidConstraintSubType, type);
3272+
}
3273+
}
3274+
};
32613275
// Validate that the sub type of a constraint is in valid form.
32623276
//
32633277
if (auto subDeclRef = isDeclRefTypeOf<Decl>(type.type))
@@ -3276,8 +3290,7 @@ bool SemanticsDeclHeaderVisitor::validateGenericConstraintSubType(
32763290
auto dependentGeneric = getShared()->getDependentGenericParent(subDeclRef);
32773291
if (dependentGeneric.getDecl() != decl->parentDecl)
32783292
{
3279-
if (sink)
3280-
sink->diagnose(type.exp, Diagnostics::invalidConstraintSubType, type);
3293+
diagnose();
32813294
return false;
32823295
}
32833296
}
@@ -3296,8 +3309,7 @@ bool SemanticsDeclHeaderVisitor::validateGenericConstraintSubType(
32963309
auto lookupDeclRef = as<LookupDeclRef>(subDeclRef.declRefBase);
32973310
if (!lookupDeclRef)
32983311
{
3299-
if (sink)
3300-
sink->diagnose(type.exp, Diagnostics::invalidConstraintSubType, type);
3312+
diagnose();
33013313
return false;
33023314
}
33033315

@@ -3311,8 +3323,7 @@ bool SemanticsDeclHeaderVisitor::validateGenericConstraintSubType(
33113323
auto baseType = as<Type>(lookupDeclRef->getLookupSource());
33123324
if (!baseType)
33133325
{
3314-
if (sink)
3315-
sink->diagnose(type.exp, Diagnostics::invalidConstraintSubType, type);
3326+
diagnose();
33163327
return false;
33173328
}
33183329
type.type = baseType;
@@ -3323,8 +3334,7 @@ bool SemanticsDeclHeaderVisitor::validateGenericConstraintSubType(
33233334
{
33243335
// It is meaningless for certain types to be used in type constraints.
33253336
// For example, `IFoo<T>` should not appear as the left-hand-side of a generic constraint.
3326-
if (sink)
3327-
sink->diagnose(type.exp, Diagnostics::invalidConstraintSubType, type);
3337+
diagnose();
33283338
return false;
33293339
}
33303340
return true;
@@ -3452,19 +3462,9 @@ void SemanticsDeclHeaderVisitor::visitGenericTypeConstraintDecl(GenericTypeConst
34523462
// Check for forward references in generic constraints after type translation
34533463
checkForwardReferencesInGenericConstraint(decl);
34543464

3455-
bool equalityCannon = true;
34563465
if (decl->isEqualityConstraint)
34573466
{
3458-
equalityCannon = checkGenericTypeEqualityCanonicalOrder(decl);
3459-
}
3460-
3461-
bool validSub = validateGenericConstraintSubType(decl, decl->sub, getSink());
3462-
if (decl->isEqualityConstraint)
3463-
{
3464-
if (!validSub && !equalityCannon)
3465-
{
3466-
getSink()->diagnose(decl, Diagnostics::failedEqualityConstraintCanonicalOrder);
3467-
}
3467+
checkGenericTypeEqualityConstraintSubType(decl);
34683468
if (!isProperConstraineeType(decl->sup) && !as<ErrorType>(decl->sup.type))
34693469
{
34703470
getSink()->diagnose(
@@ -3475,6 +3475,7 @@ void SemanticsDeclHeaderVisitor::visitGenericTypeConstraintDecl(GenericTypeConst
34753475
}
34763476
else
34773477
{
3478+
validateGenericConstraintSubType(decl, decl->sub, getSink());
34783479
if (!isValidGenericConstraintType(decl->sup) && !as<ErrorType>(decl->sup.type))
34793480
{
34803481
getSink()->diagnose(
@@ -3487,11 +3488,12 @@ void SemanticsDeclHeaderVisitor::visitGenericTypeConstraintDecl(GenericTypeConst
34873488
}
34883489

34893490
ContainerDecl* findDeclsLowestCommonAncestor(Decl*& a, Decl*& b);
3491+
int compareDecls(Decl* lhs, Decl* rhs);
34903492

3491-
bool SemanticsDeclHeaderVisitor::checkGenericTypeEqualityCanonicalOrder(
3493+
void SemanticsDeclHeaderVisitor::checkGenericTypeEqualityConstraintSubType(
34923494
GenericTypeConstraintDecl* decl)
34933495
{
3494-
auto compare = [&]() -> int
3496+
auto checkAndCompare = [&]() -> int
34953497
{
34963498
bool subOk = validateGenericConstraintSubType(decl, decl->sub);
34973499
bool supOk = validateGenericConstraintSubType(decl, decl->sup);
@@ -3502,8 +3504,10 @@ bool SemanticsDeclHeaderVisitor::checkGenericTypeEqualityCanonicalOrder(
35023504
}
35033505
else if (!(subOk || supOk))
35043506
{
3505-
// None is qualified, the generic constraint is not valid anyway.
3506-
// There is no point in comparing further.
3507+
getSink()->diagnose(decl, Diagnostics::noValidEqualityConstraintSubType);
3508+
// Re-run the validation to emit the diagnostic this time
3509+
validateGenericConstraintSubType(decl, decl->sub, getSink());
3510+
validateGenericConstraintSubType(decl, decl->sup, getSink());
35073511
return -1;
35083512
}
35093513
// Both sub and sup are qualified
@@ -3517,7 +3521,7 @@ bool SemanticsDeclHeaderVisitor::checkGenericTypeEqualityCanonicalOrder(
35173521
auto ancestor = findDeclsLowestCommonAncestor(subAncestor, supAncestor);
35183522
if (!ancestor)
35193523
{
3520-
return 0;
3524+
return compareDecls(subAncestor, supAncestor);
35213525
}
35223526

35233527
auto subIndex = ancestor->getMembers().binarySearch(subAncestor);
@@ -3526,17 +3530,16 @@ bool SemanticsDeclHeaderVisitor::checkGenericTypeEqualityCanonicalOrder(
35263530
return int(supIndex - subIndex);
35273531
};
35283532

3529-
bool res = true;
3530-
int cmp = compare();
3533+
int cmp = checkAndCompare();
35313534
if (cmp > 0)
35323535
{
35333536
Swap(decl->sub, decl->sup);
35343537
}
3535-
if (cmp == 0 && decl->sub != decl->sup)
3538+
else if (cmp == 0 && decl->sub != decl->sup)
35363539
{
3537-
res = false;
3540+
// The comparison was not fully handled for this case.
3541+
getSink()->diagnose(decl, Diagnostics::failedEqualityConstraintCanonicalOrder);
35383542
}
3539-
return res;
35403543
}
35413544

35423545
void SemanticsDeclHeaderVisitor::visitGenericTypeParamDecl(GenericTypeParamDecl* decl)

source/slang/slang-diagnostic-defs.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1693,7 +1693,18 @@ DIAGNOSTIC(
16931693
"type '$0' is not a proper type to use in a generic equality constraint.")
16941694
DIAGNOSTIC(
16951695
30405,
1696+
Error,
1697+
noValidEqualityConstraintSubType,
1698+
"generic equality constraint requires at least one operand to be dependant on the generic "
1699+
"declaration")
1700+
DIAGNOSTIC(
1701+
30402,
16961702
Note,
1703+
invalidEqualityConstraintSubType,
1704+
"type '$0' cannot be constrained by a type equality")
1705+
DIAGNOSTIC(
1706+
30407,
1707+
Warning,
16971708
failedEqualityConstraintCanonicalOrder,
16981709
"failed to resolve canonical order of generic equality constraint.")
16991710

0 commit comments

Comments
 (0)