Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

improve behaviour of inheritance #2220

Merged
merged 2 commits into from
Mar 20, 2022
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
27 changes: 27 additions & 0 deletions src/ast/analysis/ComponentLookup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,32 @@ const Component* ComponentLookupAnalysis::getComponent(
// search nested scopes bottom up
const Component* searchScope = scope;
while (searchScope != nullptr) {
// search in components declared in scope
for (const Component* cur : searchScope->getComponents()) {
if (cur->getComponentType()->getName() == toString(boundName)) {
return cur;
}
}

// also search in bases
for (const auto& baseType : searchScope->getBaseComponents()) {
const auto found = enclosingComponent.find(searchScope);
const Component* const scopeEnclosingComponent =
(found == enclosingComponent.end() ? nullptr : found->second);
// search base component
const Component* const base =
getComponent(scopeEnclosingComponent, baseType->getName(), activeBinding);
if (base == searchScope) {
return nullptr;
}
if (base != nullptr) {
const Component* const found = getComponent(base, name, activeBinding);
if (found) {
return found;
}
}
}

auto found = enclosingComponent.find(searchScope);
if (found != enclosingComponent.end()) {
searchScope = found->second;
Expand All @@ -76,4 +97,10 @@ const Component* ComponentLookupAnalysis::getComponent(
return nullptr;
}

const Component* ComponentLookupAnalysis::getEnclosingComponent(const Component* comp) const {
const auto found = enclosingComponent.find(comp);
const Component* const enclosing = (found == enclosingComponent.end() ? nullptr : found->second);
return enclosing;
}

} // namespace souffle::ast::analysis
8 changes: 8 additions & 0 deletions src/ast/analysis/ComponentLookup.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,14 @@ class ComponentLookupAnalysis : public Analysis {
const Component* getComponent(
const Component* scope, const std::string& name, const TypeBinding& activeBinding) const;

/**
* Return the component enclosing the given component.
*
* @param comp the enclosed component.
* @return a pointer to the enclosing component, or null if the component is in the global scope.
*/
const Component* getEnclosingComponent(const Component* comp) const;

private:
// components defined outside of any components
std::set<const Component*> globalScopeComponents;
Expand Down
62 changes: 31 additions & 31 deletions src/ast/analysis/typesystem/TypeConstraints.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,30 +79,30 @@ TypeConstraint hasSuperTypeInSet(const TypeVar& var, TypeSet values) {

C(TypeVar var, TypeSet values) : var(std::move(var)), values(std::move(values)) {}

bool update(Assignment<TypeVar>& assigment) const override {
bool update(Assignment<TypeVar>& assignment) const override {
// get current value of variable a
TypeSet& assigments = assigment[var];
TypeSet& assignments = assignment[var];

// remove all types that are not sub-types of b
if (assigments.isAll()) {
assigments = values;
if (assignments.isAll()) {
assignments = values;
return true;
}

TypeSet newAssigments;
for (const Type& type : assigments) {
TypeSet newAssignments;
for (const Type& type : assignments) {
assert(!isA<AliasType>(type));
bool existsSuperTypeInValues =
any_of(values, [&type](const Type& value) { return isSubtypeOf(type, value); });
if (existsSuperTypeInValues) {
newAssigments.insert(type);
newAssignments.insert(type);
}
}
// check whether there was a change
if (newAssigments == assigments) {
if (newAssignments == assignments) {
return false;
}
assigments = newAssigments;
assignments = newAssignments;
return true;
}

Expand All @@ -124,10 +124,10 @@ TypeConstraint subtypesOfTheSameBaseType(const TypeVar& left, const TypeVar& rig

C(TypeVar left, TypeVar right) : left(std::move(left)), right(std::move(right)) {}

bool update(Assignment<TypeVar>& assigment) const override {
bool update(Assignment<TypeVar>& assignment) const override {
// get current value of variable a
TypeSet& assigmentsLeft = assigment[left];
TypeSet& assigmentsRight = assigment[right];
TypeSet& assignmentsLeft = assignment[left];
TypeSet& assignmentsRight = assignment[right];

// Base types common to left and right variables.
TypeSet baseTypes;
Expand All @@ -138,17 +138,17 @@ TypeConstraint subtypesOfTheSameBaseType(const TypeVar& left, const TypeVar& rig

// Iterate over possible types extracting base types.
// Left
if (!assigmentsLeft.isAll()) {
for (const Type& type : assigmentsLeft) {
if (!assignmentsLeft.isAll()) {
for (const Type& type : assignmentsLeft) {
assert(!isA<AliasType>(type));
if (isA<SubsetType>(type) || isA<ConstantType>(type)) {
baseTypesLeft.insert(getBaseType(&type));
}
}
}
// Right
if (!assigmentsRight.isAll()) {
for (const Type& type : assigmentsRight) {
if (!assignmentsRight.isAll()) {
for (const Type& type : assignmentsRight) {
assert(!isA<AliasType>(type));
if (isA<SubsetType>(type) || isA<ConstantType>(type)) {
baseTypesRight.insert(getBaseType(&type));
Expand All @@ -160,24 +160,24 @@ TypeConstraint subtypesOfTheSameBaseType(const TypeVar& left, const TypeVar& rig
TypeSet resultRight;

// Handle all
if (assigmentsLeft.isAll() && assigmentsRight.isAll()) {
if (assignmentsLeft.isAll() && assignmentsRight.isAll()) {
return false;
}

// If left xor right is all, assign base types of the other side as possible values.
if (assigmentsLeft.isAll()) {
assigmentsLeft = baseTypesRight;
if (assignmentsLeft.isAll()) {
assignmentsLeft = baseTypesRight;
return true;
}
if (assigmentsRight.isAll()) {
assigmentsRight = baseTypesLeft;
if (assignmentsRight.isAll()) {
assignmentsRight = baseTypesLeft;
return true;
}

baseTypes = TypeSet::intersection(baseTypesLeft, baseTypesRight);

// Allow types if they are subtypes of any of the common base types.
for (const Type& type : assigmentsLeft) {
for (const Type& type : assignmentsLeft) {
assert(!isA<AliasType>(type));
bool isSubtypeOfCommonBaseType = any_of(baseTypes.begin(), baseTypes.end(),
[&type](const Type& baseType) { return isSubtypeOf(type, baseType); });
Expand All @@ -186,7 +186,7 @@ TypeConstraint subtypesOfTheSameBaseType(const TypeVar& left, const TypeVar& rig
}
}

for (const Type& type : assigmentsRight) {
for (const Type& type : assignmentsRight) {
assert(!isA<AliasType>(type));
bool isSubtypeOfCommonBaseType = any_of(baseTypes.begin(), baseTypes.end(),
[&type](const Type& baseType) { return isSubtypeOf(type, baseType); });
Expand All @@ -196,11 +196,11 @@ TypeConstraint subtypesOfTheSameBaseType(const TypeVar& left, const TypeVar& rig
}

// check whether there was a change
if (resultLeft == assigmentsLeft && resultRight == assigmentsRight) {
if (resultLeft == assignmentsLeft && resultRight == assignmentsRight) {
return false;
}
assigmentsLeft = resultLeft;
assigmentsRight = resultRight;
assignmentsLeft = resultLeft;
assignmentsRight = resultRight;
return true;
}
//
Expand Down Expand Up @@ -238,14 +238,14 @@ TypeConstraint satisfiesOverload(const TypeEnvironment& typeEnv, IntrinsicFuncto
: typeEnv(typeEnv), overloads(std::move(overloads)), result(std::move(result)),
args(std::move(args)), subtypeResult(subtypeResult) {}

bool update(Assignment<TypeVar>& assigment) const override {
bool update(Assignment<TypeVar>& assignment) const override {
auto subtypesOf = [&](const TypeSet& src, TypeAttribute tyAttr) {
auto& ty = typeEnv.getConstantType(tyAttr);
return src.filter(TypeSet(true), [&](auto&& x) { return isSubtypeOf(x, ty); });
};

auto possible = [&](TypeAttribute ty, const TypeVar& var) {
auto& curr = assigment[var];
auto& curr = assignment[var];
return curr.isAll() || any_of(curr, [&](auto&& t) { return getTypeAttribute(t) == ty; });
};

Expand All @@ -272,7 +272,7 @@ TypeConstraint satisfiesOverload(const TypeEnvironment& typeEnv, IntrinsicFuncto
if (overload.op != FunctorOp::ORD) {
for (std::size_t i = 0; i < args.size(); ++i) {
auto argTy = overload.params[overload.variadic ? 0 : i];
auto& currArg = assigment[args[i]];
auto& currArg = assignment[args[i]];
auto newArg = subtypesOf(currArg, argTy);
changed |= currArg != newArg;
// 2020-05-09: CI linter says to remove `std::move`, but clang-tidy-10 is happy.
Expand All @@ -281,15 +281,15 @@ TypeConstraint satisfiesOverload(const TypeEnvironment& typeEnv, IntrinsicFuncto
}

if (nonMonotonicUpdate || subtypeResult) {
return subtypesOf(assigment[result], overload.result);
return subtypesOf(assignment[result], overload.result);
} else {
nonMonotonicUpdate = true;
return TypeSet{typeEnv.getConstantType(overload.result)};
}
}();

if (newResult) {
auto& curr = assigment[result];
auto& curr = assignment[result];
changed |= curr != *newResult;
// 2020-05-09: CI linter says to remove `std::move`, but clang-tidy-10 is happy.
curr = std::move(*newResult); // NOLINT
Expand Down
53 changes: 32 additions & 21 deletions src/ast/transform/ComponentInstantiation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,8 @@ struct ComponentContent {
*/
ComponentContent getInstantiatedContent(Program& program, const ComponentInit& componentInit,
const Component* enclosingComponent, const ComponentLookupAnalysis& componentLookup,
VecOwn<Clause>& orphans, ErrorReport& report, const TypeBinding& binding = analysis::TypeBinding(),
VecOwn<Clause>& orphans, const std::set<std::string>& overridden, ErrorReport& report,
const TypeBinding& binding = analysis::TypeBinding(),
unsigned int maxDepth = MAX_INSTANTIATION_DEPTH);

/**
Expand All @@ -136,20 +137,27 @@ void collectContent(Program& program, const Component& component, const TypeBind
ComponentContent& res, VecOwn<Clause>& orphans, const std::set<std::string>& overridden,
ErrorReport& report, unsigned int maxInstantiationDepth) {
// start with relations and clauses of the base components
std::set<std::string> superOverridden;
superOverridden.insert(overridden.begin(), overridden.end());
superOverridden.insert(component.getOverridden().begin(), component.getOverridden().end());

for (const auto& base : component.getBaseComponents()) {
const Component* comp = componentLookup.getComponent(enclosingComponent, base->getName(), binding);
if (comp != nullptr) {
const Component* baseComp =
componentLookup.getComponent(enclosingComponent, base->getName(), binding);

if (baseComp != nullptr) {
// link formal with actual type parameters
const auto& formalParams = comp->getComponentType()->getTypeParameters();
const auto& formalParams = baseComp->getComponentType()->getTypeParameters();
const auto& actualParams = base->getTypeParameters();

// update type binding
TypeBinding activeBinding = binding.extend(formalParams, actualParams);

for (const auto& cur : comp->getInstantiations()) {
for (const auto& cur : baseComp->getInstantiations()) {
// instantiate sub-component
ComponentContent content = getInstantiatedContent(program, *cur, enclosingComponent,
componentLookup, orphans, report, activeBinding, maxInstantiationDepth - 1);
const std::set<std::string> noOverridden;
ComponentContent content = getInstantiatedContent(program, *cur, baseComp, componentLookup,
orphans, noOverridden, report, activeBinding, maxInstantiationDepth - 1);

// process types
for (auto& type : content.types) {
Expand All @@ -173,11 +181,10 @@ void collectContent(Program& program, const Component& component, const TypeBind
}

// collect definitions from base type
std::set<std::string> superOverridden;
superOverridden.insert(overridden.begin(), overridden.end());
superOverridden.insert(component.getOverridden().begin(), component.getOverridden().end());
collectContent(program, *comp, activeBinding, comp, componentLookup, res, orphans,
superOverridden, report, maxInstantiationDepth);
collectContent(program, *baseComp, activeBinding, componentLookup.getEnclosingComponent(baseComp),
componentLookup, res, orphans, superOverridden, report, maxInstantiationDepth);
} else {
// base component cannot be found => triggers a semantic error
}
}

Expand Down Expand Up @@ -282,8 +289,8 @@ void collectContent(Program& program, const Component& component, const TypeBind
}

// add the local clauses
// TODO: check orphans
for (const auto& cur : component.getClauses()) {
// do not add clauses for overridden relations
if (overridden.count(cur->getHead()->getQualifiedName().getQualifiers()[0]) == 0) {
Relation* rel = index[cur->getHead()->getQualifiedName()];
if (rel != nullptr) {
Expand All @@ -296,6 +303,7 @@ void collectContent(Program& program, const Component& component, const TypeBind
}

// add orphan clauses at the current level if they can be resolved
// TODO regardless of the overridden relations ?
for (auto iter = orphans.begin(); iter != orphans.end();) {
auto& cur = *iter;
Relation* rel = index[cur->getHead()->getQualifiedName()];
Expand All @@ -312,7 +320,8 @@ void collectContent(Program& program, const Component& component, const TypeBind

ComponentContent getInstantiatedContent(Program& program, const ComponentInit& componentInit,
const Component* enclosingComponent, const ComponentLookupAnalysis& componentLookup,
VecOwn<Clause>& orphans, ErrorReport& report, const TypeBinding& binding, unsigned int maxDepth) {
VecOwn<Clause>& orphans, const std::set<std::string>& overridden, ErrorReport& report,
const TypeBinding& binding, unsigned int maxDepth) {
// start with an empty list
ComponentContent res;

Expand All @@ -324,21 +333,22 @@ ComponentContent getInstantiatedContent(Program& program, const ComponentInit& c
// get referenced component
const Component* component = componentLookup.getComponent(
enclosingComponent, componentInit.getComponentType()->getName(), binding);

if (component == nullptr) {
// this component is not defined => will trigger a semantic error
return res;
}

// update type biding
// update type binding
const auto& formalParams = component->getComponentType()->getTypeParameters();
const auto& actualParams = componentInit.getComponentType()->getTypeParameters();
TypeBinding activeBinding = binding.extend(formalParams, actualParams);

// instantiated nested components
for (const auto& cur : component->getInstantiations()) {
// get nested content
ComponentContent nestedContent = getInstantiatedContent(
program, *cur, component, componentLookup, orphans, report, activeBinding, maxDepth - 1);
ComponentContent nestedContent = getInstantiatedContent(program, *cur, component, componentLookup,
orphans, overridden, report, activeBinding, maxDepth - 1);

// add types
for (auto& type : nestedContent.types) {
Expand All @@ -362,9 +372,8 @@ ComponentContent getInstantiatedContent(Program& program, const ComponentInit& c
}

// collect all content in this component
std::set<std::string> overridden;
collectContent(program, *component, activeBinding, enclosingComponent, componentLookup, res, orphans,
overridden, report, maxDepth);
collectContent(program, *component, activeBinding, componentLookup.getEnclosingComponent(component),
componentLookup, res, orphans, overridden, report, maxDepth);

// update user-defined type names
std::map<QualifiedName, QualifiedName> typeNameMapping;
Expand Down Expand Up @@ -525,8 +534,10 @@ bool ComponentInstantiationTransformer::transform(TranslationUnit& translationUn

for (const auto* cur : program.getComponentInstantiations()) {
VecOwn<Clause> orphans;
const std::set<std::string> overridden;

auto content = getInstantiatedContent(program, *cur, nullptr, componentLookup, orphans, report);
auto content =
getInstantiatedContent(program, *cur, nullptr, componentLookup, orphans, overridden, report);
if (report.getNumErrors() != 0) continue;

for (auto& type : content.types) {
Expand Down
1 change: 1 addition & 0 deletions tests/evaluation/comp-override2/b.Rel.csv
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
0
2
3
3 changes: 2 additions & 1 deletion tests/evaluation/comp-override2/comp-override2.dl
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
.decl Rel(x:number) overridable
.output Rel()
.comp AA {
Rel(0).
Rel(0). // should this clause be discarded by override declared in B when
// AA is instantiated as a side-effect of B inheriting from A ?
}

.init aa = AA
Expand Down
6 changes: 6 additions & 0 deletions tests/semantic/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -238,3 +238,9 @@ if (FALSE)
positive_test(complex_rule)
endif ()
positive_test(compose_components)
positive_test(inherit_argument)
positive_test(inherit_argument_override)
positive_test(inherit_instance)
positive_test(inherit_instance_override)
positive_test(instantiate_from_inherited)
positive_test(instantiate_from_inherited_override)
Loading