Skip to content

Commit

Permalink
Dyno: fix decls with tuple type expressions and initialization expres…
Browse files Browse the repository at this point in the history
…sions (chapel-lang#25951)

Closes Cray/chapel-private#6385; prior to this
PR, the following program:

```Chapel
var x: 2*int = (7,3);
```

Produced an error:

```
─── error in onetuple.chpl:1 [IncompatibleTypeAndInit] ───
  Type mismatch between declared type of 'x' and initialization expression.
  In the following declaration:
      |
    1 | var x: 2*int = (7,3);
      |        ⎺⎺⎺⎺⎺   ⎺⎺⎺⎺⎺
      |
  the type specifier has type '(int(64), int(64))', while the initial value has type '(int(64), int(64))'.
```

The (chapel syntax) output is quite confusing in the error message, but
the difference between the specified type and the actual type are the
intents of the components. On the left hand site we have a 'type' tuple,
but on the right we have a value tuple.

There is already logic on `main` to adjust the type of the declaration
_at the end_ to mark the tuple components 'var' or 'ref', etc. However,
the type-value mismatch happens while the type of the declaration is
being inferred, before the final adjustment. The solution seems to be to
adjust the type of the tuple's components using the same rules as
"usual" (described, I believe, in the [specification
here](https://chapel-lang.org/docs/language/spec/tuples.html#tuple-argument-intents)).
So, this PR extracts the "adjustment" process, runs it once on the type
expression to ensure it's compatible with the value, then again on the
final type.

Reviewed by @benharsh -- thanks!

## Testing
- [x] dyno tests
  • Loading branch information
DanilaFe authored Sep 30, 2024
2 parents 7022668 + 550c076 commit c77aedc
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 23 deletions.
73 changes: 50 additions & 23 deletions frontend/lib/resolution/Resolver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1348,6 +1348,42 @@ static const Type* computeVarArgTuple(Resolver& resolver,
return typePtr;
}


static bool adjustTupleTypeIntentForDecl(Context* context,
const NamedDecl* decl,
QualifiedType::Kind declaredKind,
QualifiedType::Kind& qtKind,
const Type*& typePtr) {
// adjust tuple declarations for value / referential tuples
if (typePtr != nullptr && decl->isVarArgFormal() == false) {
if (auto tupleType = typePtr->toTupleType()) {
if (declaredKind == QualifiedType::DEFAULT_INTENT) {
typePtr = tupleType->toReferentialTuple(context);
qtKind = QualifiedType::CONST_REF;
return true;
} else if (declaredKind == QualifiedType::CONST_INTENT) {
typePtr = tupleType->toReferentialTuple(context, /* makeConst */ true);
qtKind = QualifiedType::CONST_REF;
return true;
} else if (qtKind == QualifiedType::CONST_IN ||
qtKind == QualifiedType::CONST_REF) {
typePtr = tupleType->toValueTuple(context, /* makeConst */ true);
return true;
} else if (qtKind == QualifiedType::VAR ||
qtKind == QualifiedType::CONST_VAR ||
qtKind == QualifiedType::REF ||
qtKind == QualifiedType::IN ||
qtKind == QualifiedType::OUT ||
qtKind == QualifiedType::INOUT ||
qtKind == QualifiedType::TYPE) {
typePtr = tupleType->toValueTuple(context);
return true;
}
}
}
return false;
}

/* If the type is generic with defaults, computes the defaults of a type.
Returns the original type if instantiating with defaults isn't necessary. */
static QualifiedType computeTypeDefaults(Resolver& resolver,
Expand Down Expand Up @@ -1577,6 +1613,19 @@ void Resolver::resolveNamedDecl(const NamedDecl* decl, const Type* useType) {
computeFormalIntent(decl, qtKind, typeExprT.type(), typeExprT.param());
}
}

// The type expression is a type-tuple (e.g., (type int, type bool)),
// but the actual value may be value-tuple (e.g., (int, bool)), or a ref
// tuple, etc. Need to adjust the intents so that getTypeForDecl (which
// runs canPass) doesn't balk at the mismatch.
auto adjustedQtKind = qtKind;
auto adjustedTypePtr = typeExprT.type();
if (adjustTupleTypeIntentForDecl(context, decl, qtKind,
adjustedQtKind, adjustedTypePtr)) {
typeExprT = QualifiedType(typeExprT.kind(), adjustedTypePtr, typeExprT.param());
}

//
// Check that the initExpr type is compatible with declared type
// Check kinds are OK
// Handle any implicit conversions / instantiations
Expand Down Expand Up @@ -1608,29 +1657,7 @@ void Resolver::resolveNamedDecl(const NamedDecl* decl, const Type* useType) {
qtKind, typePtr);
}

// adjust tuple declarations for value / referential tuples
if (typePtr != nullptr && decl->isVarArgFormal() == false) {
if (auto tupleType = typePtr->toTupleType()) {
if (declaredKind == QualifiedType::DEFAULT_INTENT) {
typePtr = tupleType->toReferentialTuple(context);
qtKind = QualifiedType::CONST_REF;
} else if (declaredKind == QualifiedType::CONST_INTENT) {
typePtr = tupleType->toReferentialTuple(context, /* makeConst */ true);
qtKind = QualifiedType::CONST_REF;
} else if (qtKind == QualifiedType::CONST_IN ||
qtKind == QualifiedType::CONST_REF) {
typePtr = tupleType->toValueTuple(context, /* makeConst */ true);
} else if (qtKind == QualifiedType::VAR ||
qtKind == QualifiedType::CONST_VAR ||
qtKind == QualifiedType::REF ||
qtKind == QualifiedType::IN ||
qtKind == QualifiedType::OUT ||
qtKind == QualifiedType::INOUT ||
qtKind == QualifiedType::TYPE) {
typePtr = tupleType->toValueTuple(context);
}
}
}
adjustTupleTypeIntentForDecl(context, decl, declaredKind, qtKind, typePtr);

ResolvedExpression& result = byPostorder.byAst(decl);
result.setType(QualifiedType(qtKind, typePtr, paramPtr));
Expand Down
27 changes: 27 additions & 0 deletions frontend/test/resolution/testTuples.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -972,6 +972,32 @@ static void test20() {
}
}

static void test21() {
// Ensure that (type int, type int) tuples in type expression are properly
// handled when they are specifying the type of var or const variable.

Context ctx;
Context* context = &ctx;
auto program = R"""(
var x: 2*int = (7,3);
const y: 2*int = (7,3);
var x2: 2*int;
const y2: 2*int;
param firstMatch = x.type == x2.type;
param secondMatch = y.type == y2.type;
)""";

auto vars = resolveTypesOfVariables(context, program,
{"x", "y", "x2", "y2", "firstMatch", "secondMatch"});
assert(vars["x"].type()->isTupleType());
assert(vars["y"].type()->isTupleType());
assert(vars["x2"].type()->isTupleType());
assert(vars["y2"].type()->isTupleType());
ensureParamBool(vars["firstMatch"], true);
ensureParamBool(vars["secondMatch"], true);
}


int main() {
test1();
Expand Down Expand Up @@ -999,5 +1025,6 @@ int main() {
testTupleGeneric();

test20();
test21();
return 0;
}

0 comments on commit c77aedc

Please sign in to comment.