diff --git a/src/org/rascalmpl/compiler/lang/rascalcore/check/CollectDataDeclaration.rsc b/src/org/rascalmpl/compiler/lang/rascalcore/check/CollectDataDeclaration.rsc index 2f0dcd52f02..104bb394871 100644 --- a/src/org/rascalmpl/compiler/lang/rascalcore/check/CollectDataDeclaration.rsc +++ b/src/org/rascalmpl/compiler/lang/rascalcore/check/CollectDataDeclaration.rsc @@ -54,6 +54,7 @@ int dataCounter = 0; void dataDeclaration(Tags tags, Declaration current, list[Variant] variants, Collector c){ userType = current.user; + currentModuleName = str s := c.top(key_current_module) ? s : ""; adtName = prettyPrintName(userType.name); tagsMap = getTags(tags); @@ -75,19 +76,36 @@ void dataDeclaration(Tags tags, Declaration current, list[Variant] variants, Col c.define(userType.name, dataId(), current, dt); adtParentScope = c.getScope(); - c.enterScope(current); - c.push(currentAdt, ); - beginDefineOrReuseTypeParameters(c, closed=true); - collect(typeParameters, c); - if(!isEmpty(commonKeywordParameterList)){ - collect(commonKeywordParameterList, c); - } - endDefineOrReuseTypeParameters(c); - - // visit all the variants in the parent scope of the data declaration + + c.push(currentAdt, ); + NestedScopes nestedScopes = newNestedScopes(c); + beginDefineOrReuseTypeParameters(c, closed=true); + collect(typeParameters, c); + if(!isEmpty(commonKeywordParameterList)){ + nestedScopes.enter(current.commonKeywordParameters); + declaredFieldNames = {}; + for(KeywordFormal kwf <- commonKeywordParameterList){ + // Collect default expression (of current common keyword + // field) in outer scope + collect(kwf.\type, kwf.expression, c); + // Define current common keyword field, and collect + // next common keyword fields (in subsequent iterations, + // if any), in inner scope(s) + nestedScopes.enter(kwf); + declaredFieldNames = defineField( + c, kwf, keywordFieldId(), declaredFieldNames, + moreHashContribs = [currentModuleName, adtName]); + c.fact(kwf, kwf.\type); + } + } + endDefineOrReuseTypeParameters(c); + // Note: Don't leave scopes of common keyword fields yet, so they are + // accessible in the variants. + + nestedScopes.enter(current); collect(variants, c); - c.pop(currentAdt); - c.leaveScope(current); + nestedScopes.leaveAll(); + c.pop(currentAdt); } AType(Solver) makeFieldType(str fieldName, Tree fieldType) @@ -133,27 +151,14 @@ void collect(current:(Variant) ` ( <{TypeArg ","}* arguments> ( <{TypeArg ","}* arguments> ; +} + +private set[str] defineField(Collector c, Tree fieldDef, IdRole fieldIdRole, set[str] declaredFieldIds, list[value] moreHashContribs = []) { + if ((TypeArg) ` ` := fieldDef || + (KeywordFormal) ` = ` := fieldDef) { + + str fieldOrgId = ""; + str fieldId = prettyPrintName(fieldOrgId); + if (fieldId in declaredFieldIds) c.report(error(fieldDef, "Double declaration of field `%v`", fieldId)); + declaredFieldIds += fieldId; + + DefInfo fieldDefInfo = defType([fieldType], makeFieldType(fieldId, fieldType)); + fieldDefInfo.md5 = normalizedMD5Hash([fieldId, fieldType, *moreHashContribs]); + + c.define(name, fieldIdRole, fieldDef, fieldDefInfo); + } else { + throw "Cannot define field: ``"; + } + + return declaredFieldIds; +} \ No newline at end of file diff --git a/src/org/rascalmpl/compiler/lang/rascalcore/check/tests/ChangeAndHashTests.rsc b/src/org/rascalmpl/compiler/lang/rascalcore/check/tests/ChangeAndHashTests.rsc index b40dc9ed3e0..4053e151b20 100644 --- a/src/org/rascalmpl/compiler/lang/rascalcore/check/tests/ChangeAndHashTests.rsc +++ b/src/org/rascalmpl/compiler/lang/rascalcore/check/tests/ChangeAndHashTests.rsc @@ -282,10 +282,10 @@ test bool nearClonesDifferentParameterNameOK() = checkModuleOK(" // Data declarations test bool commonKwFieldAdded() - = expectEqual("data D;", "data D(int x = 0);"); + = expectSubset("data D;", "data D(int x = 0);"); -test bool commenKwFieldDeleted() - = expectEqual("data D(int x = 0);", "data D;"); +test bool commonKwFieldDeleted() + = expectSuperset("data D(int x = 0);", "data D;"); test bool consAdded() = expectSubset("data D;", "data D = d(int n);"); diff --git a/src/org/rascalmpl/compiler/lang/rascalcore/check/tests/UseDefTests.rsc b/src/org/rascalmpl/compiler/lang/rascalcore/check/tests/UseDefTests.rsc index 8a812d28544..f86065936e7 100644 --- a/src/org/rascalmpl/compiler/lang/rascalcore/check/tests/UseDefTests.rsc +++ b/src/org/rascalmpl/compiler/lang/rascalcore/check/tests/UseDefTests.rsc @@ -107,4 +107,45 @@ test bool overloadedSyntaxField(){ return useDefOK(mtext, ("c": <0, {2}>)) // check uses of first declaration of c && useDefOK(mtext, ("c": <1, {3}>)) // check uses of first declaration of c ; -} \ No newline at end of file +} + +test bool issue2193() = + useDefOK("module Issue2193 + data D(int foo = 1) = d(int bar = 2); + void main() { + D x = d(); + x.foo = 16; + y = x.foo; + }", + ( + "D": <0, {1}>, + "foo": <0, {1, 2}>, + "d": <0, {1}>, + "x": <0, {1, 2}> + )); + +test bool issue2193Regression1(){ + mtext = "module Issue2193Regression1 + data D = d(int i); + data E(D d = d(5));"; + return useDefOK(mtext, ("d": <0, {2}>)) // check uses of first declaration of d + && useDefOK(mtext, ("d": <1, {}>)) // check uses of second declaration of d + && useDefOK(mtext, ( + "D": <0, {1}>, + "i": <0, {}>, + "E": <0, {}> + )); +} + +test bool issue2193Regression2(){ + mtext = "module Issue2193Regression1 + data E(D d = d(5)); + data D = d(int i);"; + return useDefOK(mtext, ("d": <0, {}>)) // check uses of first declaration of d + && useDefOK(mtext, ("d": <2, {1}>)) // check uses of second declaration of d + && useDefOK(mtext, ( + "E": <0, {}>, + "D": <1, {0}>, + "i": <0, {}> + )); +}