Skip to content

Commit

Permalink
Fix generic type dyn (#782)
Browse files Browse the repository at this point in the history
  • Loading branch information
marcauberer authored Feb 15, 2025
1 parent c2f93e9 commit 0962e4f
Show file tree
Hide file tree
Showing 12 changed files with 57 additions and 20 deletions.
10 changes: 9 additions & 1 deletion media/test-project/test.spice
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
f<int> main() {
type T int|dyn|double;

f<int> foo<T>(T t) {
return 1;
}

f<int> main() {
foo(1);
foo(1.0);
foo("test");
}
25 changes: 16 additions & 9 deletions src/model/GenericType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,31 +9,38 @@ namespace spice::compiler {
*
* @param qualType Qualified qualType to be checked
* @param ignoreArraySize Ignore the array size for qualType comparison
* @param igoreQualifiers Ignore the type qualifiers for qualType comparison
* @param ignoreQualifiers Ignore the type qualifiers for qualType comparison
* @return True or false
*/
bool GenericType::checkConditionsOf(const QualType &qualType, bool ignoreArraySize, bool igoreQualifiers) const {
return checkTypeConditionOf(qualType, ignoreArraySize, igoreQualifiers);
bool GenericType::checkConditionsOf(const QualType &qualType, bool ignoreArraySize, bool ignoreQualifiers) const {
return checkTypeConditionOf(qualType, ignoreArraySize, ignoreQualifiers);
}

/**
* Checks if the given qualType matches all qualType conditions to get a manifestation of the current generic qualType
*
* @param qualType Qualified type to be checked
* @param requestedType Qualified type to be checked
* @param ignoreArraySize Ignore the array size for qualType comparison
* @param ignoreQualifiers Ignore the type qualifiers for qualType comparison
* @return True or false
*/
bool GenericType::checkTypeConditionOf(const QualType &qualType, bool ignoreArraySize, bool ignoreQualifiers) const {
// Succeed if no qualType conditions are set
bool GenericType::checkTypeConditionOf(const QualType &requestedType, bool ignoreArraySize, bool ignoreQualifiers) const {
// Succeed if there are no type conditions
if (typeConditions.empty())
return true;
// Succeed if the given qual type is generic and matches the current one
if (qualType.hasAnyGenericParts() && qualType == *this)
if (requestedType.hasAnyGenericParts() && requestedType == *this)
return true;
// Check type conditions
return std::ranges::any_of(typeConditions, [&](const QualType &typeCondition) {
return typeCondition.is(TY_DYN) || typeCondition.matches(qualType, ignoreArraySize, ignoreQualifiers, ignoreQualifiers);
return std::ranges::any_of(typeConditions, [&](QualType typeCondition) {
// If we have a dyn type condition, the conditions are fulfilled immediately
if (typeCondition.is(TY_DYN))
return true;

// In situations like this we need to unwrap: requestedType = const int&, typeCondition = int
QualType requestedTypeCopy = requestedType;
unwrapBoth(typeCondition, requestedTypeCopy);
return typeCondition.matches(requestedTypeCopy, ignoreArraySize, ignoreQualifiers, ignoreQualifiers);
});
}

Expand Down
6 changes: 3 additions & 3 deletions src/model/GenericType.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class GenericType : public QualType {

// Public methods
[[nodiscard]] bool checkConditionsOf(const QualType &qualType, bool ignoreArraySize = false,
bool igoreQualifiers = false) const;
bool ignoreQualifiers = false) const;

// Public members
bool used = false;
Expand All @@ -36,7 +36,7 @@ class GenericType : public QualType {
QualTypeList typeConditions = {QualType(TY_DYN)};

// Private methods
[[nodiscard]] bool checkTypeConditionOf(const QualType &qualType, bool ignoreArraySize, bool ignoreQualifiers) const;
[[nodiscard]] bool checkTypeConditionOf(const QualType &requestedType, bool ignoreArraySize, bool ignoreQualifiers) const;
};

} // namespace spice::compiler
} // namespace spice::compiler
1 change: 0 additions & 1 deletion src/symboltablebuilder/Type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -576,7 +576,6 @@ bool Type::matches(const Type *otherType, bool ignoreArraySize) const {
return false;
}

// Ignore or compare qualifiers
return true;
}

Expand Down
6 changes: 2 additions & 4 deletions src/typechecker/FunctionManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -115,10 +115,8 @@ Function *FunctionManager::insertSubstantiation(Scope *insertScope, const Functi
// Check if the function exists already
for (const auto &manifestations : insertScope->functions | std::views::values) {
if (manifestations.contains(signature)) {
if (newManifestation.isFunction())
throw SemanticError(declNode, FUNCTION_DECLARED_TWICE, "'" + signature + "' is declared twice");
else
throw SemanticError(declNode, PROCEDURE_DECLARED_TWICE, "'" + signature + "' is declared twice");
const SemanticErrorType errorType = newManifestation.isFunction() ? FUNCTION_DECLARED_TWICE : PROCEDURE_DECLARED_TWICE;
throw SemanticError(declNode, errorType, "'" + signature + "' is declared twice");
}
}

Expand Down
10 changes: 8 additions & 2 deletions src/typechecker/TypeCheckerTopLevelDefinitionsPrepare.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -546,8 +546,7 @@ std::any TypeChecker::visitGenericTypeDefPrepare(GenericTypeDefNode *node) {
for (const auto &typeAlt : node->typeAltsLst->dataTypes) {
auto typeCondition = std::any_cast<QualType>(visit(typeAlt));
HANDLE_UNRESOLVED_TYPE_PTR(typeCondition)
if (!typeCondition.is(TY_DYN))
typeConditions.push_back(typeCondition);
typeConditions.push_back(typeCondition);
}

// Add generic type to the scope
Expand All @@ -559,6 +558,13 @@ std::any TypeChecker::visitGenericTypeDefPrepare(GenericTypeDefNode *node) {
sourceFile->compilerOutput.warnings.emplace_back(node->typeAltsLst->codeLoc, SINGLE_GENERIC_TYPE_CONDITION,
"Generic type is locked to one type");

// Check if the list contains the dyn type, along with other types
const bool containsDynType = std::ranges::any_of(typeConditions, [&](const QualType &type) { return type.is(TY_DYN); });
if (containsDynType && typeConditions.size() > 1)
sourceFile->compilerOutput.warnings.emplace_back(
node->typeAltsLst->codeLoc, INEFFECTIVE_GENERIC_TYPE_CONDITION,
"One or several type conditions are superfluous, because the dyn type condition is given");

return nullptr;
}

Expand Down
2 changes: 2 additions & 0 deletions src/util/CompilerWarning.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ std::string CompilerWarning::getMessagePrefix(CompilerWarningType warningType) {
return "Identity cast";
case SINGLE_GENERIC_TYPE_CONDITION:
return "Only one type condition";
case INEFFECTIVE_GENERIC_TYPE_CONDITION:
return "Ineffective type condition";
case BOOL_ASSIGN_AS_CONDITION:
return "Bool assignment as condition";
case ASYNC_LAMBDA_CAPTURE_RULE_VIOLATION:
Expand Down
1 change: 1 addition & 0 deletions src/util/CompilerWarning.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ enum CompilerWarningType : uint8_t {
SHADOWED_VARIABLE,
IDENTITY_CAST,
SINGLE_GENERIC_TYPE_CONDITION,
INEFFECTIVE_GENERIC_TYPE_CONDITION,
BOOL_ASSIGN_AS_CONDITION,
ASYNC_LAMBDA_CAPTURE_RULE_VIOLATION,
UNINSTALL_FAILED,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0
11 changes: 11 additions & 0 deletions test/test-files/typechecker/generics/success-generics/source.spice
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
type T int|dyn|double;

f<int> foo<T>(T t) {
return 1;
}

f<int> main() {
foo(1);
foo(1.0);
foo("test");
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
type T int|dyn|double;

f<int> main() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[Warning] ./source.spice:1:8: Ineffective type condition: One or several type conditions are superfluous, because the dyn type condition is given

0 comments on commit 0962e4f

Please sign in to comment.